4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
10 * Note: the style MF_MOUSESELECT is used to mark popup items that
11 * have been selected, i.e. their popup menu is currently displayed.
12 * This is probably not the meaning this style has in MS-Windows.
16 #include "wine/port.h"
26 #include "wine/winbase16.h"
27 #include "wine/winuser16.h"
28 #include "wine/unicode.h"
31 #include "nonclient.h"
35 #include "debugtools.h"
37 DEFAULT_DEBUG_CHANNEL(menu);
38 DECLARE_DEBUG_CHANNEL(accel);
40 /* internal popup menu window messages */
42 #define MM_SETMENUHANDLE (WM_USER + 0)
43 #define MM_GETMENUHANDLE (WM_USER + 1)
45 /* Menu item structure */
47 /* ----------- MENUITEMINFO Stuff ----------- */
48 UINT fType; /* Item type. */
49 UINT fState; /* Item state. */
50 UINT wID; /* Item id. */
51 HMENU hSubMenu; /* Pop-up menu. */
52 HBITMAP hCheckBit; /* Bitmap when checked. */
53 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
54 LPWSTR text; /* Item text or bitmap handle. */
55 DWORD dwItemData; /* Application defined. */
56 DWORD dwTypeData; /* depends on fMask */
57 HBITMAP hbmpItem; /* bitmap in win98 style menus */
58 /* ----------- Wine stuff ----------- */
59 RECT rect; /* Item area (relative to menu window) */
60 UINT xTab; /* X position of text after Tab */
63 /* Popup menu structure */
65 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
66 WORD wMagic; /* Magic number */
67 WORD Width; /* Width of the whole menu */
68 WORD Height; /* Height of the whole menu */
69 UINT nItems; /* Number of items in the menu */
70 HWND hWnd; /* Window containing the menu */
71 MENUITEM *items; /* Array of menu items */
72 UINT FocusedItem; /* Currently focused item */
73 HWND hwndOwner; /* window receiving the messages for ownerdraw */
74 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
75 /* ------------ MENUINFO members ------ */
76 DWORD dwStyle; /* Extended mennu style */
77 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
78 HBRUSH hbrBack; /* brush for menu background */
79 DWORD dwContextHelpID;
80 DWORD dwMenuData; /* application defined value */
81 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
82 } POPUPMENU, *LPPOPUPMENU;
84 /* internal flags for menu tracking */
86 #define TF_ENDMENU 0x0001
87 #define TF_SUSPENDPOPUP 0x0002
88 #define TF_SKIPREMOVE 0x0004
93 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
94 HMENU hTopMenu; /* initial menu */
95 HWND hOwnerWnd; /* where notifications are sent */
99 #define MENU_MAGIC 0x554d /* 'MU' */
104 /* Internal MENU_TrackMenu() flags */
105 #define TPM_INTERNAL 0xF0000000
106 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
107 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
108 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
110 /* popup menu shade thickness */
111 #define POPUP_XSHADE 4
112 #define POPUP_YSHADE 4
114 /* Space between 2 menu bar items */
115 #define MENU_BAR_ITEMS_SPACE 12
117 /* Minimum width of a tab character */
118 #define MENU_TAB_SPACE 8
120 /* Height of a separator item */
121 #define SEPARATOR_HEIGHT 5
123 /* (other menu->FocusedItem values give the position of the focused item) */
124 #define NO_SELECTED_ITEM 0xffff
126 #define MENU_ITEM_TYPE(flags) \
127 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
129 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
130 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
131 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
133 #define IS_SYSTEM_MENU(menu) \
134 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
136 #define IS_SYSTEM_POPUP(menu) \
137 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
139 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
140 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
141 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
142 MF_POPUP | MF_SYSMENU | MF_HELP)
143 #define STATE_MASK (~TYPE_MASK)
145 /* Dimension of the menu bitmaps */
146 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
148 static HBITMAP hStdMnArrow = 0;
150 /* Minimze/restore/close buttons to be inserted in menubar */
151 static HBITMAP hBmpMinimize = 0;
152 static HBITMAP hBmpMinimizeD = 0;
153 static HBITMAP hBmpMaximize = 0;
154 static HBITMAP hBmpMaximizeD = 0;
155 static HBITMAP hBmpClose = 0;
156 static HBITMAP hBmpCloseD = 0;
159 static HBRUSH hShadeBrush = 0;
160 static HFONT hMenuFont = 0;
161 static HFONT hMenuFontBold = 0;
163 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
165 /* Use global popup window because there's no way 2 menus can
166 * be tracked at the same time. */
167 static HWND top_popup;
169 /* Flag set by EndMenu() to force an exit from menu tracking */
170 static BOOL fEndMenu = FALSE;
172 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
175 /*********************************************************************
176 * menu class descriptor
178 const struct builtin_class_descr MENU_builtin_class =
180 POPUPMENU_CLASS_ATOM, /* name */
181 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
182 NULL, /* procA (winproc is Unicode only) */
183 PopupMenuWndProc, /* procW */
184 sizeof(HMENU), /* extra */
185 IDC_ARROWA, /* cursor */
186 COLOR_MENU+1 /* brush */
190 /***********************************************************************
191 * debug_print_menuitem
193 * Print a menuitem in readable form.
196 #define debug_print_menuitem(pre, mp, post) \
197 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
199 #define MENUOUT(text) \
200 DPRINTF("%s%s", (count++ ? "," : ""), (text))
202 #define MENUFLAG(bit,text) \
204 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
207 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
210 TRACE("%s ", prefix);
212 UINT flags = mp->fType;
213 int typ = MENU_ITEM_TYPE(flags);
214 DPRINTF( "{ ID=0x%x", mp->wID);
215 if (flags & MF_POPUP)
216 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
220 if (typ == MFT_STRING)
222 else if (typ == MFT_SEPARATOR)
224 else if (typ == MFT_OWNERDRAW)
226 else if (typ == MFT_BITMAP)
232 MENUFLAG(MF_POPUP, "pop");
233 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
234 MENUFLAG(MFT_MENUBREAK, "brk");
235 MENUFLAG(MFT_RADIOCHECK, "radio");
236 MENUFLAG(MFT_RIGHTORDER, "rorder");
237 MENUFLAG(MF_SYSMENU, "sys");
238 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
241 DPRINTF( "+0x%x", flags);
246 DPRINTF( ", State=");
247 MENUFLAG(MFS_GRAYED, "grey");
248 MENUFLAG(MFS_DEFAULT, "default");
249 MENUFLAG(MFS_DISABLED, "dis");
250 MENUFLAG(MFS_CHECKED, "check");
251 MENUFLAG(MFS_HILITE, "hi");
252 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
253 MENUFLAG(MF_MOUSESELECT, "mouse");
255 DPRINTF( "+0x%x", flags);
258 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
260 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
262 if (typ == MFT_STRING) {
264 DPRINTF( ", Text=%s", debugstr_w(mp->text));
266 DPRINTF( ", Text=Null");
267 } else if (mp->text == NULL)
270 DPRINTF( ", Text=%p", mp->text);
272 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
278 DPRINTF(" %s\n", postfix);
285 /***********************************************************************
288 * Validate the given menu handle and returns the menu structure pointer.
290 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
292 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
293 if (!menu || menu->wMagic != MENU_MAGIC)
295 WARN("invalid menu handle=%x, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
301 /***********************************************************************
304 * Get the system menu of a window
306 static HMENU get_win_sys_menu( HWND hwnd )
309 WND *win = WIN_FindWndPtr( hwnd );
313 WIN_ReleaseWndPtr( win );
318 /***********************************************************************
321 * Return the default system menu.
323 static HMENU MENU_CopySysPopup(void)
325 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
328 POPUPMENU* menu = MENU_GetMenu(hMenu);
329 menu->wFlags |= MF_SYSMENU | MF_POPUP;
330 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
333 ERR("Unable to load default system menu\n" );
335 TRACE("returning %x.\n", hMenu );
341 /**********************************************************************
344 * Create a copy of the system menu. System menu in Windows is
345 * a special menu bar with the single entry - system menu popup.
346 * This popup is presented to the outside world as a "system menu".
347 * However, the real system menu handle is sometimes seen in the
348 * WM_MENUSELECT parameters (and Word 6 likes it this way).
350 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
354 if ((hMenu = CreateMenu()))
356 POPUPMENU *menu = MENU_GetMenu(hMenu);
357 menu->wFlags = MF_SYSMENU;
358 menu->hWnd = WIN_GetFullHandle( hWnd );
360 if (hPopupMenu == (HMENU)(-1))
361 hPopupMenu = MENU_CopySysPopup();
362 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
366 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
368 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
369 menu->items[0].fState = 0;
370 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
372 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
375 DestroyMenu( hMenu );
377 ERR("failed to load system menu!\n");
382 /***********************************************************************
385 * Menus initialisation.
390 NONCLIENTMETRICSA ncm;
392 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
397 /* Load menu bitmaps */
398 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
399 /* Load system buttons bitmaps */
400 hBmpMinimize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCE));
401 hBmpMinimizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCED));
402 hBmpMaximize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORE));
403 hBmpMaximizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORED));
404 hBmpClose = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSE));
405 hBmpCloseD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSED));
410 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
411 arrow_bitmap_width = bm.bmWidth;
412 arrow_bitmap_height = bm.bmHeight;
416 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
419 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
422 DeleteObject( hBitmap );
423 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
426 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
427 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
430 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
433 ncm.lfMenuFont.lfWeight += 300;
434 if ( ncm.lfMenuFont.lfWeight > 1000)
435 ncm.lfMenuFont.lfWeight = 1000;
437 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
443 /***********************************************************************
444 * MENU_InitSysMenuPopup
446 * Grey the appropriate items in System menu.
448 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
452 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
453 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
454 gray = ((style & WS_MAXIMIZE) != 0);
455 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
456 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
457 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
458 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
459 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
460 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
461 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
462 gray = (clsStyle & CS_NOCLOSE) != 0;
464 /* The menu item must keep its state if it's disabled */
466 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
470 /******************************************************************************
472 * UINT MENU_GetStartOfNextColumn(
475 *****************************************************************************/
477 static UINT MENU_GetStartOfNextColumn(
480 POPUPMENU *menu = MENU_GetMenu(hMenu);
484 return NO_SELECTED_ITEM;
486 i = menu->FocusedItem + 1;
487 if( i == NO_SELECTED_ITEM )
490 for( ; i < menu->nItems; ++i ) {
491 if (menu->items[i].fType & MF_MENUBARBREAK)
495 return NO_SELECTED_ITEM;
499 /******************************************************************************
501 * UINT MENU_GetStartOfPrevColumn(
504 *****************************************************************************/
506 static UINT MENU_GetStartOfPrevColumn(
509 POPUPMENU *menu = MENU_GetMenu(hMenu);
513 return NO_SELECTED_ITEM;
515 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
516 return NO_SELECTED_ITEM;
518 /* Find the start of the column */
520 for(i = menu->FocusedItem; i != 0 &&
521 !(menu->items[i].fType & MF_MENUBARBREAK);
525 return NO_SELECTED_ITEM;
527 for(--i; i != 0; --i) {
528 if (menu->items[i].fType & MF_MENUBARBREAK)
532 TRACE("ret %d.\n", i );
539 /***********************************************************************
542 * Find a menu item. Return a pointer on the item, and modifies *hmenu
543 * in case the item was in a sub-menu.
545 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
550 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
551 if (wFlags & MF_BYPOSITION)
553 if (*nPos >= menu->nItems) return NULL;
554 return &menu->items[*nPos];
558 MENUITEM *item = menu->items;
559 for (i = 0; i < menu->nItems; i++, item++)
561 if (item->wID == *nPos)
566 else if (item->fType & MF_POPUP)
568 HMENU hsubmenu = item->hSubMenu;
569 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
581 /***********************************************************************
584 * Find a Sub menu. Return the position of the submenu, and modifies
585 * *hmenu in case it is found in another sub-menu.
586 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
588 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
593 if (((*hmenu)==0xffff) ||
594 (!(menu = MENU_GetMenu(*hmenu))))
595 return NO_SELECTED_ITEM;
597 for (i = 0; i < menu->nItems; i++, item++) {
598 if(!(item->fType & MF_POPUP)) continue;
599 if (item->hSubMenu == hSubTarget) {
603 HMENU hsubmenu = item->hSubMenu;
604 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
605 if (pos != NO_SELECTED_ITEM) {
611 return NO_SELECTED_ITEM;
614 /***********************************************************************
617 static void MENU_FreeItemData( MENUITEM* item )
620 if (IS_STRING_ITEM(item->fType) && item->text)
621 HeapFree( GetProcessHeap(), 0, item->text );
624 /***********************************************************************
625 * MENU_FindItemByCoords
627 * Find the item at the specified coordinates (screen coords). Does
628 * not work for child windows and therefore should not be called for
629 * an arbitrary system menu.
631 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
632 POINT pt, UINT *pos )
638 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
639 pt.x -= wrect.left;pt.y -= wrect.top;
641 for (i = 0; i < menu->nItems; i++, item++)
643 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
644 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
654 /***********************************************************************
657 * Find the menu item selected by a key press.
658 * Return item id, -1 if none, -2 if we should close the menu.
660 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
661 UINT key, BOOL forceMenuChar )
663 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
665 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
669 POPUPMENU *menu = MENU_GetMenu( hmenu );
670 MENUITEM *item = menu->items;
678 for (i = 0; i < menu->nItems; i++, item++)
680 if (item->text && (IS_STRING_ITEM(item->fType)))
682 WCHAR *p = item->text - 2;
685 p = strchrW (p + 2, '&');
687 while (p != NULL && p [1] == '&');
688 if (p && (toupper(p[1]) == key)) return i;
692 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
693 MAKEWPARAM( key, menu->wFlags ), hmenu );
694 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
695 if (HIWORD(menuchar) == 1) return (UINT)(-2);
699 /***********************************************************************
702 * Load the bitmap associated with the magic menu item and its style
705 static HBITMAP MENU_LoadMagicItem(UINT id, BOOL hilite, DWORD dwItemData)
708 * Magic menu item id's section
709 * These magic id's are used by windows to insert "standard" mdi
710 * buttons (minimize,restore,close) on menu. Under windows,
711 * these magic id's make sure the right things appear when those
712 * bitmap buttons are pressed/selected/released.
716 { case HBMMENU_SYSTEM:
717 return (dwItemData) ?
718 (HBITMAP)dwItemData :
719 (hilite ? hBmpMinimizeD : hBmpMinimize);
720 case HBMMENU_MBAR_RESTORE:
721 return (hilite ? hBmpMaximizeD: hBmpMaximize);
722 case HBMMENU_MBAR_MINIMIZE:
723 return (hilite ? hBmpMinimizeD : hBmpMinimize);
724 case HBMMENU_MBAR_CLOSE:
725 return (hilite ? hBmpCloseD : hBmpClose);
726 case HBMMENU_CALLBACK:
727 case HBMMENU_MBAR_CLOSE_D:
728 case HBMMENU_MBAR_MINIMIZE_D:
729 case HBMMENU_POPUP_CLOSE:
730 case HBMMENU_POPUP_RESTORE:
731 case HBMMENU_POPUP_MAXIMIZE:
732 case HBMMENU_POPUP_MINIMIZE:
734 FIXME("Magic 0x%08x not implemented\n", id);
740 /***********************************************************************
743 * Calculate the size of the menu item and store it in lpitem->rect.
745 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
746 INT orgX, INT orgY, BOOL menuBar )
749 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
751 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
752 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
753 (menuBar ? " (MenuBar)" : ""));
755 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
757 if (lpitem->fType & MF_OWNERDRAW)
760 ** Experimentation under Windows reveals that an owner-drawn
761 ** menu is expected to return the size of the content part of
762 ** the menu item, not including the checkmark nor the submenu
763 ** arrow. Windows adds those values itself and returns the
764 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
766 MEASUREITEMSTRUCT mis;
767 mis.CtlType = ODT_MENU;
769 mis.itemID = lpitem->wID;
770 mis.itemData = (DWORD)lpitem->dwItemData;
773 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
774 lpitem->rect.right += mis.itemWidth;
778 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
781 /* under at least win95 you seem to be given a standard
782 height for the menu and the height value is ignored */
784 if (TWEAK_WineLook == WIN31_LOOK)
785 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
787 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
790 lpitem->rect.bottom += mis.itemHeight;
792 TRACE("id=%04x size=%dx%d\n",
793 lpitem->wID, mis.itemWidth, mis.itemHeight);
794 /* Fall through to get check/arrow width calculation. */
797 if (lpitem->fType & MF_SEPARATOR)
799 lpitem->rect.bottom += SEPARATOR_HEIGHT;
805 lpitem->rect.right += 2 * check_bitmap_width;
806 if (lpitem->fType & MF_POPUP)
807 lpitem->rect.right += arrow_bitmap_width;
810 if (lpitem->fType & MF_OWNERDRAW)
813 if (IS_BITMAP_ITEM(lpitem->fType))
818 /* Check if there is a magic menu item associated with this item */
819 if (IS_MAGIC_ITEM(lpitem->text))
821 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fType & MF_HILITE),
825 resBmp = (HBITMAP)lpitem->text;
827 if (GetObjectA(resBmp, sizeof(bm), &bm ))
829 lpitem->rect.right += bm.bmWidth;
830 lpitem->rect.bottom += bm.bmHeight;
831 if (TWEAK_WineLook == WIN98_LOOK) {
832 /* Leave space for the sunken border */
833 lpitem->rect.right += 2;
834 lpitem->rect.bottom += 2;
841 /* it must be a text item - unless it's the system menu */
842 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
845 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
847 lpitem->rect.right += size.cx;
848 if (TWEAK_WineLook == WIN31_LOOK)
849 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
851 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
856 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
858 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
860 /* Item contains a tab (only meaningful in popup menus) */
861 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
862 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
863 lpitem->rect.right += MENU_TAB_SPACE;
867 if (strchrW( lpitem->text, '\b' ))
868 lpitem->rect.right += MENU_TAB_SPACE;
869 lpitem->xTab = lpitem->rect.right - check_bitmap_width
870 - arrow_bitmap_width;
873 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
877 /***********************************************************************
878 * MENU_PopupMenuCalcSize
880 * Calculate the size of a popup menu.
882 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
887 int orgX, orgY, maxX, maxTab, maxTabWidth;
889 lppop->Width = lppop->Height = 0;
890 if (lppop->nItems == 0) return;
893 SelectObject( hdc, hMenuFont);
896 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
898 while (start < lppop->nItems)
900 lpitem = &lppop->items[start];
902 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
904 maxTab = maxTabWidth = 0;
906 /* Parse items until column break or end of menu */
907 for (i = start; i < lppop->nItems; i++, lpitem++)
910 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
912 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
914 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
915 maxX = max( maxX, lpitem->rect.right );
916 orgY = lpitem->rect.bottom;
917 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
919 maxTab = max( maxTab, lpitem->xTab );
920 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
924 /* Finish the column (set all items to the largest width found) */
925 maxX = max( maxX, maxTab + maxTabWidth );
926 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
928 lpitem->rect.right = maxX;
929 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
930 lpitem->xTab = maxTab;
933 lppop->Height = max( lppop->Height, orgY );
938 /* space for 3d border */
939 if(TWEAK_WineLook > WIN31_LOOK)
949 /***********************************************************************
950 * MENU_MenuBarCalcSize
952 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
953 * height is off by 1 pixel which causes lengthy window relocations when
954 * active document window is maximized/restored.
956 * Calculate the size of the menu bar.
958 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
959 LPPOPUPMENU lppop, HWND hwndOwner )
962 int start, i, orgX, orgY, maxY, helpPos;
964 if ((lprect == NULL) || (lppop == NULL)) return;
965 if (lppop->nItems == 0) return;
966 TRACE("left=%d top=%d right=%d bottom=%d\n",
967 lprect->left, lprect->top, lprect->right, lprect->bottom);
968 lppop->Width = lprect->right - lprect->left;
970 maxY = lprect->top+1;
973 while (start < lppop->nItems)
975 lpitem = &lppop->items[start];
979 /* Parse items until line break or end of menu */
980 for (i = start; i < lppop->nItems; i++, lpitem++)
982 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
984 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
986 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
988 debug_print_menuitem (" item: ", lpitem, "");
989 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
991 if (lpitem->rect.right > lprect->right)
993 if (i != start) break;
994 else lpitem->rect.right = lprect->right;
996 maxY = max( maxY, lpitem->rect.bottom );
997 orgX = lpitem->rect.right;
1000 /* Finish the line (set all items to the largest height found) */
1001 while (start < i) lppop->items[start++].rect.bottom = maxY;
1004 lprect->bottom = maxY;
1005 lppop->Height = lprect->bottom - lprect->top;
1007 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1008 /* the last item (if several lines, only move the last line) */
1009 lpitem = &lppop->items[lppop->nItems-1];
1010 orgY = lpitem->rect.top;
1011 orgX = lprect->right;
1012 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1013 if ( (helpPos==-1) || (helpPos>i) )
1015 if (lpitem->rect.top != orgY) break; /* Other line */
1016 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1017 lpitem->rect.left += orgX - lpitem->rect.right;
1018 lpitem->rect.right = orgX;
1019 orgX = lpitem->rect.left;
1023 /***********************************************************************
1026 * Draw a single menu item.
1028 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1029 UINT height, BOOL menuBar, UINT odaction )
1033 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1035 if (lpitem->fType & MF_SYSMENU)
1037 if( !IsIconic(hwnd) ) {
1038 if (TWEAK_WineLook > WIN31_LOOK)
1039 NC_DrawSysButton95( hwnd, hdc,
1041 (MF_HILITE | MF_MOUSESELECT) );
1043 NC_DrawSysButton( hwnd, hdc,
1045 (MF_HILITE | MF_MOUSESELECT) );
1051 if (lpitem->fType & MF_OWNERDRAW)
1054 ** Experimentation under Windows reveals that an owner-drawn
1055 ** menu is given the rectangle which includes the space it requested
1056 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1057 ** and a popup-menu arrow. This is the value of lpitem->rect.
1058 ** Windows will leave all drawing to the application except for
1059 ** the popup-menu arrow. Windows always draws that itself, after
1060 ** the menu owner has finished drawing.
1064 dis.CtlType = ODT_MENU;
1066 dis.itemID = lpitem->wID;
1067 dis.itemData = (DWORD)lpitem->dwItemData;
1069 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1070 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED;
1071 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1072 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1073 dis.hwndItem = (HWND)hmenu;
1075 dis.rcItem = lpitem->rect;
1076 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1077 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1078 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1079 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1081 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1082 /* Fall through to draw popup-menu arrow */
1085 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1086 lpitem->rect.right,lpitem->rect.bottom);
1088 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1090 rect = lpitem->rect;
1092 if (!(lpitem->fType & MF_OWNERDRAW))
1094 if (lpitem->fState & MF_HILITE)
1096 if(TWEAK_WineLook == WIN98_LOOK)
1099 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1101 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1103 else /* Not Win98 Look */
1105 if(!IS_BITMAP_ITEM(lpitem->fType))
1106 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1110 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1113 SetBkMode( hdc, TRANSPARENT );
1115 if (!(lpitem->fType & MF_OWNERDRAW))
1117 /* vertical separator */
1118 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1120 if (TWEAK_WineLook > WIN31_LOOK)
1124 rc.bottom = height - 3;
1125 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1129 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1130 MoveToEx( hdc, rect.left, 0, NULL );
1131 LineTo( hdc, rect.left, height );
1135 /* horizontal separator */
1136 if (lpitem->fType & MF_SEPARATOR)
1138 if (TWEAK_WineLook > WIN31_LOOK)
1143 rc.top += SEPARATOR_HEIGHT / 2;
1144 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1148 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1149 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1150 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1158 if (lpitem->fState & MF_HILITE)
1160 if(TWEAK_WineLook == WIN98_LOOK)
1163 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1164 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1166 if(lpitem->fState & MF_GRAYED)
1167 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1169 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1170 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1173 else /* Not Win98 Look */
1175 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1176 if(!IS_BITMAP_ITEM(lpitem->fType))
1177 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1182 if (lpitem->fState & MF_GRAYED)
1183 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1185 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1186 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1189 /* helper lines for debugging */
1190 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1191 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1192 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1193 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1198 INT y = rect.top + rect.bottom;
1199 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1200 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1202 if (!(lpitem->fType & MF_OWNERDRAW))
1204 /* Draw the check mark
1207 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1209 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1210 if (bm) /* we have a custom bitmap */
1212 HDC hdcMem = CreateCompatibleDC( hdc );
1213 SelectObject( hdcMem, bm );
1214 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1215 check_bitmap_width, check_bitmap_height,
1216 hdcMem, 0, 0, SRCCOPY );
1219 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1222 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1223 HDC hdcMem = CreateCompatibleDC( hdc );
1224 SelectObject( hdcMem, bm );
1225 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1226 DrawFrameControl( hdcMem, &r, DFC_MENU,
1227 (lpitem->fType & MFT_RADIOCHECK) ?
1228 DFCS_MENUBULLET : DFCS_MENUCHECK );
1229 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1230 hdcMem, 0, 0, SRCCOPY );
1236 /* Draw the popup-menu arrow */
1237 if (lpitem->fType & MF_POPUP)
1239 HDC hdcMem = CreateCompatibleDC( hdc );
1240 HBITMAP hOrigBitmap;
1242 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1243 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1244 (y - arrow_bitmap_height) / 2,
1245 arrow_bitmap_width, arrow_bitmap_height,
1246 hdcMem, 0, 0, SRCCOPY );
1247 SelectObject( hdcMem, hOrigBitmap );
1251 rect.left += check_bitmap_width;
1252 rect.right -= arrow_bitmap_width;
1255 /* Done for owner-drawn */
1256 if (lpitem->fType & MF_OWNERDRAW)
1259 /* Draw the item text or bitmap */
1260 if (IS_BITMAP_ITEM(lpitem->fType))
1267 HDC hdcMem = CreateCompatibleDC( hdc );
1270 * Check if there is a magic menu item associated with this item
1271 * and load the appropriate bitmap
1273 if (IS_MAGIC_ITEM(lpitem->text))
1275 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fState & MF_HILITE),
1276 lpitem->dwItemData);
1279 resBmp = (HBITMAP)lpitem->text;
1284 GetObjectA( resBmp, sizeof(bm), &bm );
1286 SelectObject(hdcMem,resBmp );
1288 /* handle fontsize > bitmap_height */
1289 h=rect.bottom - rect.top;
1290 top = (h>bm.bmHeight) ?
1291 rect.top+(h-bm.bmHeight)/2 : rect.top;
1292 w=rect.right - rect.left;
1294 if (TWEAK_WineLook == WIN95_LOOK) {
1295 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
1296 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
1297 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1301 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
1303 BitBlt( hdc, left, top, w,
1312 /* No bitmap - process text if present */
1313 else if (IS_STRING_ITEM(lpitem->fType))
1318 UINT uFormat = (menuBar) ?
1319 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1320 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1322 if ( lpitem->fState & MFS_DEFAULT )
1324 hfontOld = SelectObject( hdc, hMenuFontBold);
1329 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1330 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1331 i = strlenW( lpitem->text );
1335 for (i = 0; lpitem->text[i]; i++)
1336 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1340 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1342 if (!(lpitem->fState & MF_HILITE) )
1344 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1345 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1346 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1347 --rect.left; --rect.top; --rect.right; --rect.bottom;
1349 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1352 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1354 /* paint the shortcut text */
1355 if (lpitem->text[i]) /* There's a tab or flush-right char */
1357 if (lpitem->text[i] == '\t')
1359 rect.left = lpitem->xTab;
1360 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1364 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1367 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1369 if (!(lpitem->fState & MF_HILITE) )
1371 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1372 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1373 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1374 --rect.left; --rect.top; --rect.right; --rect.bottom;
1376 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1378 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1382 SelectObject (hdc, hfontOld);
1387 /***********************************************************************
1388 * MENU_DrawPopupMenu
1390 * Paint a popup menu.
1392 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1394 HBRUSH hPrevBrush = 0;
1397 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1399 GetClientRect( hwnd, &rect );
1401 if(TWEAK_WineLook == WIN31_LOOK)
1403 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1404 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1407 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1408 && (SelectObject( hdc, hMenuFont)))
1412 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1414 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1420 /* draw 3-d shade */
1421 if(TWEAK_WineLook == WIN31_LOOK) {
1422 SelectObject( hdc, hShadeBrush );
1423 SetBkMode( hdc, TRANSPARENT );
1424 ropPrev = SetROP2( hdc, R2_MASKPEN );
1426 i = rect.right; /* why SetBrushOrg() doesn't? */
1427 PatBlt( hdc, i & 0xfffffffe,
1428 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1429 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1430 rect.bottom - rect.top, 0x00a000c9 );
1432 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1433 i & 0xfffffffe,rect.right - rect.left,
1434 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1435 SelectObject( hdc, hPrevPen );
1436 SelectObject( hdc, hPrevBrush );
1437 SetROP2( hdc, ropPrev );
1440 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1442 /* draw menu items */
1444 menu = MENU_GetMenu( hmenu );
1445 if (menu && menu->nItems)
1450 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1451 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1452 menu->Height, FALSE, ODA_DRAWENTIRE );
1457 SelectObject( hdc, hPrevBrush );
1462 /***********************************************************************
1465 * Paint a menu bar. Returns the height of the menu bar.
1466 * called from [windows/nonclient.c]
1468 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1474 HMENU hMenu = GetMenu(hwnd);
1476 lppop = MENU_GetMenu( hMenu );
1477 if (lppop == NULL || lprect == NULL)
1479 retvalue = GetSystemMetrics(SM_CYMENU);
1483 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1485 hfontOld = SelectObject( hDC, hMenuFont);
1487 if (lppop->Height == 0)
1488 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1490 lprect->bottom = lprect->top + lppop->Height;
1494 retvalue = lppop->Height;
1498 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1500 if (TWEAK_WineLook == WIN31_LOOK)
1502 SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1503 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1504 LineTo( hDC, lprect->right, lprect->bottom );
1508 SelectObject( hDC, GetSysColorPen(COLOR_3DFACE));
1509 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1510 LineTo( hDC, lprect->right, lprect->bottom );
1513 if (lppop->nItems == 0)
1515 retvalue = GetSystemMetrics(SM_CYMENU);
1519 for (i = 0; i < lppop->nItems; i++)
1521 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
1522 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1524 retvalue = lppop->Height;
1527 if (hfontOld) SelectObject (hDC, hfontOld);
1532 /***********************************************************************
1535 * Display a popup menu.
1537 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1538 INT x, INT y, INT xanchor, INT yanchor )
1543 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1544 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1546 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1547 if (menu->FocusedItem != NO_SELECTED_ITEM)
1549 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1550 menu->FocusedItem = NO_SELECTED_ITEM;
1553 /* store the owner for DrawItem */
1554 menu->hwndOwner = hwndOwner;
1557 MENU_PopupMenuCalcSize( menu, hwndOwner );
1559 /* adjust popup menu pos so that it fits within the desktop */
1561 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1562 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1564 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1567 x -= width - xanchor;
1568 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1569 x = GetSystemMetrics(SM_CXSCREEN) - width;
1573 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1576 y -= height + yanchor;
1577 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1578 y = GetSystemMetrics(SM_CYSCREEN) - height;
1582 if( TWEAK_WineLook == WIN31_LOOK )
1584 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1585 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1588 /* NOTE: In Windows, top menu popup is not owned. */
1589 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1590 WS_POPUP, x, y, width, height,
1591 hwndOwner, 0, GetWindowLongA(hwndOwner,GWL_HINSTANCE),
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 );
1605 /***********************************************************************
1608 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1609 BOOL sendMenuSelect, HMENU topmenu )
1614 TRACE("owner=0x%04x menu=0x%04x 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);
1623 SelectObject( hdc, hMenuFont);
1625 /* Clear previous highlighted item */
1626 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1628 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1629 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1630 lppop->Height, !(lppop->wFlags & MF_POPUP),
1634 /* Highlight new item (if any) */
1635 lppop->FocusedItem = wIndex;
1636 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1638 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1639 lppop->items[wIndex].fState |= MF_HILITE;
1640 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1641 &lppop->items[wIndex], lppop->Height,
1642 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1646 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1647 SendMessageA( hwndOwner, WM_MENUSELECT,
1648 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1649 ip->fType | ip->fState | MF_MOUSESELECT |
1650 (lppop->wFlags & MF_SYSMENU)), hmenu);
1653 else if (sendMenuSelect) {
1656 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1657 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1658 MENUITEM *ip = &ptm->items[pos];
1659 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1660 ip->fType | ip->fState | MF_MOUSESELECT |
1661 (ptm->wFlags & MF_SYSMENU)), topmenu);
1665 ReleaseDC( lppop->hWnd, hdc );
1669 /***********************************************************************
1670 * MENU_MoveSelection
1672 * Moves currently selected item according to the offset parameter.
1673 * If there is no selection then it should select the last item if
1674 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1676 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1681 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1683 menu = MENU_GetMenu( hmenu );
1684 if ((!menu) || (!menu->items)) return;
1686 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1688 if( menu->nItems == 1 ) return; else
1689 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1691 if (!(menu->items[i].fType & MF_SEPARATOR))
1693 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1698 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1699 i >= 0 && i < menu->nItems ; i += offset)
1700 if (!(menu->items[i].fType & MF_SEPARATOR))
1702 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1708 /**********************************************************************
1711 * Set an item flags, id and text ptr. Called by InsertMenu() and
1714 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1717 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1719 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1720 TRACE("flags=%x str=%p\n", flags, str);
1722 if (IS_STRING_ITEM(flags))
1726 flags |= MF_SEPARATOR;
1732 /* Item beginning with a backspace is a help item */
1738 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1740 strcpyW( text, str );
1744 else if (IS_BITMAP_ITEM(flags))
1745 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1746 else item->text = NULL;
1748 if (flags & MF_OWNERDRAW)
1749 item->dwItemData = (DWORD)str;
1751 item->dwItemData = 0;
1753 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1754 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1756 if (flags & MF_POPUP)
1758 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1759 if (menu) menu->wFlags |= MF_POPUP;
1771 if (flags & MF_POPUP)
1772 item->hSubMenu = 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 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1787 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1792 /**********************************************************************
1795 * Insert a new item into a menu.
1797 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1802 if (!(menu = MENU_GetMenu(hMenu)))
1805 /* Find where to insert new item */
1807 if (flags & MF_BYPOSITION) {
1808 if (pos > menu->nItems)
1811 if (!MENU_FindItem( &hMenu, &pos, flags ))
1814 if (!(menu = MENU_GetMenu( hMenu )))
1819 /* Create new items array */
1821 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1824 WARN("allocation failed\n" );
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;
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 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1856 flags = GET_WORD(res);
1857 res += sizeof(WORD);
1858 if (!(flags & MF_POPUP))
1861 res += sizeof(WORD);
1863 if (!IS_STRING_ITEM(flags))
1864 ERR("not a string item %04x\n", flags );
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 )))
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));
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)
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();
1926 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1927 DestroyMenu(mii.hSubMenu);
1930 mii.fMask |= MIIM_SUBMENU;
1931 mii.fType |= MF_POPUP;
1933 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1934 } while (!(resinfo & MF_END));
1939 /***********************************************************************
1942 * Return the handle of the selected sub-popup menu (if any).
1944 static HMENU MENU_GetSubPopup( HMENU hmenu )
1949 menu = MENU_GetMenu( hmenu );
1951 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1953 item = &menu->items[menu->FocusedItem];
1954 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1955 return item->hSubMenu;
1960 /***********************************************************************
1961 * MENU_HideSubPopups
1963 * Hide the sub-popup menus of this menu.
1965 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1966 BOOL sendMenuSelect )
1968 POPUPMENU *menu = MENU_GetMenu( hmenu );
1970 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1972 if (menu && top_popup)
1978 if (menu->FocusedItem != NO_SELECTED_ITEM)
1980 item = &menu->items[menu->FocusedItem];
1981 if (!(item->fType & MF_POPUP) ||
1982 !(item->fState & MF_MOUSESELECT)) return;
1983 item->fState &= ~MF_MOUSESELECT;
1984 hsubmenu = item->hSubMenu;
1987 submenu = MENU_GetMenu( hsubmenu );
1988 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1989 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1990 DestroyWindow( submenu->hWnd );
1996 /***********************************************************************
1999 * Display the sub-menu of the selected item of this menu.
2000 * Return the handle of the submenu, or hmenu if no submenu to display.
2002 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2003 BOOL selectFirst, UINT wFlags )
2010 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2012 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2014 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2016 item = &menu->items[menu->FocusedItem];
2017 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2020 /* message must be sent before using item,
2021 because nearly everything may be changed by the application ! */
2023 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2024 if (!(wFlags & TPM_NONOTIFY))
2025 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2026 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2028 item = &menu->items[menu->FocusedItem];
2031 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2032 if (!(item->fState & MF_HILITE))
2034 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2035 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2037 SelectObject( hdc, hMenuFont);
2039 item->fState |= MF_HILITE;
2040 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2041 ReleaseDC( menu->hWnd, hdc );
2043 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2046 item->fState |= MF_MOUSESELECT;
2048 if (IS_SYSTEM_MENU(menu))
2050 MENU_InitSysMenuPopup(item->hSubMenu,
2051 GetWindowLongA( menu->hWnd, GWL_STYLE ),
2052 GetClassLongA( menu->hWnd, GCL_STYLE));
2054 NC_GetSysPopupPos( menu->hWnd, &rect );
2055 rect.top = rect.bottom;
2056 rect.right = GetSystemMetrics(SM_CXSIZE);
2057 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2061 GetWindowRect( menu->hWnd, &rect );
2062 if (menu->wFlags & MF_POPUP)
2064 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2065 rect.top += item->rect.top;
2066 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2067 rect.bottom = item->rect.top - item->rect.bottom;
2071 rect.left += item->rect.left;
2072 rect.top += item->rect.bottom;
2073 rect.right = item->rect.right - item->rect.left;
2074 rect.bottom = item->rect.bottom - item->rect.top;
2078 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2079 rect.left, rect.top, rect.right, rect.bottom );
2081 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2082 return item->hSubMenu;
2087 /**********************************************************************
2090 BOOL MENU_IsMenuActive(void)
2092 return (top_popup != 0);
2095 /***********************************************************************
2098 * Walks menu chain trying to find a menu pt maps to.
2100 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2102 POPUPMENU *menu = MENU_GetMenu( hMenu );
2103 UINT item = menu->FocusedItem;
2106 /* try subpopup first (if any) */
2107 ret = (item != NO_SELECTED_ITEM &&
2108 (menu->items[item].fType & MF_POPUP) &&
2109 (menu->items[item].fState & MF_MOUSESELECT))
2110 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2112 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2114 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2115 if( menu->wFlags & MF_POPUP )
2117 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2119 else if (ht == HTSYSMENU)
2120 ret = get_win_sys_menu( menu->hWnd );
2121 else if (ht == HTMENU)
2122 ret = GetMenu( menu->hWnd );
2127 /***********************************************************************
2128 * MENU_ExecFocusedItem
2130 * Execute a menu item (for instance when user pressed Enter).
2131 * Return the wID of the executed item. Otherwise, -1 indicating
2132 * that no menu item was executed;
2133 * Have to receive the flags for the TrackPopupMenu options to avoid
2134 * sending unwanted message.
2137 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2140 POPUPMENU *menu = MENU_GetMenu( hMenu );
2142 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2144 if (!menu || !menu->nItems ||
2145 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2147 item = &menu->items[menu->FocusedItem];
2149 TRACE("%08x %08x %08x\n",
2150 hMenu, item->wID, item->hSubMenu);
2152 if (!(item->fType & MF_POPUP))
2154 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2156 /* If TPM_RETURNCMD is set you return the id, but
2157 do not send a message to the owner */
2158 if(!(wFlags & TPM_RETURNCMD))
2160 if( menu->wFlags & MF_SYSMENU )
2161 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2162 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2164 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2170 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2175 /***********************************************************************
2176 * MENU_SwitchTracking
2178 * Helper function for menu navigation routines.
2180 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2182 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2183 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2185 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2187 if( pmt->hTopMenu != hPtMenu &&
2188 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2190 /* both are top level menus (system and menu-bar) */
2191 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2192 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2193 pmt->hTopMenu = hPtMenu;
2195 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2196 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2200 /***********************************************************************
2203 * Return TRUE if we can go on with menu tracking.
2205 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2207 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2212 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2215 if( IS_SYSTEM_MENU(ptmenu) )
2216 item = ptmenu->items;
2218 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2222 if( ptmenu->FocusedItem != id )
2223 MENU_SwitchTracking( pmt, hPtMenu, id );
2225 /* If the popup menu is not already "popped" */
2226 if(!(item->fState & MF_MOUSESELECT ))
2228 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2230 /* In win31, a newly popped menu always remains opened for the next buttonup */
2231 if(TWEAK_WineLook == WIN31_LOOK)
2232 ptmenu->bTimeToHide = FALSE;
2237 /* Else the click was on the menu bar, finish the tracking */
2242 /***********************************************************************
2245 * Return the value of MENU_ExecFocusedItem if
2246 * the selected item was not a popup. Else open the popup.
2247 * A -1 return value indicates that we go on with menu tracking.
2250 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2252 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2257 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2260 if( IS_SYSTEM_MENU(ptmenu) )
2261 item = ptmenu->items;
2263 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2265 if( item && (ptmenu->FocusedItem == id ))
2267 if( !(item->fType & MF_POPUP) )
2268 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2270 /* If we are dealing with the top-level menu */
2271 /* and this is a click on an already "popped" item: */
2272 /* Stop the menu tracking and close the opened submenus */
2273 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2276 ptmenu->bTimeToHide = TRUE;
2282 /***********************************************************************
2285 * Return TRUE if we can go on with menu tracking.
2287 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2289 UINT id = NO_SELECTED_ITEM;
2290 POPUPMENU *ptmenu = NULL;
2294 ptmenu = MENU_GetMenu( hPtMenu );
2295 if( IS_SYSTEM_MENU(ptmenu) )
2298 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2301 if( id == NO_SELECTED_ITEM )
2303 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2304 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2307 else if( ptmenu->FocusedItem != id )
2309 MENU_SwitchTracking( pmt, hPtMenu, id );
2310 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2316 /***********************************************************************
2319 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2321 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2323 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2325 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2326 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2328 MDINEXTMENU next_menu;
2333 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2334 next_menu.hmenuNext = 0;
2335 next_menu.hwndNext = 0;
2336 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2338 TRACE("%04x [%04x] -> %04x [%04x]\n",
2339 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2341 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2343 DWORD style = GetWindowLongA( pmt->hOwnerWnd, GWL_STYLE );
2344 hNewWnd = pmt->hOwnerWnd;
2345 if( IS_SYSTEM_MENU(menu) )
2347 /* switch to the menu bar */
2349 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2353 menu = MENU_GetMenu( hNewMenu );
2354 id = menu->nItems - 1;
2357 else if (style & WS_SYSMENU )
2359 /* switch to the system menu */
2360 hNewMenu = get_win_sys_menu( hNewWnd );
2364 else /* application returned a new menu to switch to */
2366 hNewMenu = next_menu.hmenuNext;
2367 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2369 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2371 DWORD style = GetWindowLongA( hNewWnd, GWL_STYLE );
2373 if (style & WS_SYSMENU &&
2374 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2376 /* get the real system menu */
2377 hNewMenu = get_win_sys_menu(hNewWnd);
2379 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2381 /* FIXME: Not sure what to do here;
2382 * perhaps try to track hNewMenu as a popup? */
2384 TRACE(" -- got confused.\n");
2391 if( hNewMenu != pmt->hTopMenu )
2393 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2395 if( pmt->hCurrentMenu != pmt->hTopMenu )
2396 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2399 if( hNewWnd != pmt->hOwnerWnd )
2402 pmt->hOwnerWnd = hNewWnd;
2403 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2406 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2407 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2414 /***********************************************************************
2417 * The idea is not to show the popup if the next input message is
2418 * going to hide it anyway.
2420 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2424 msg.hwnd = pmt->hOwnerWnd;
2426 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2427 pmt->trackFlags |= TF_SKIPREMOVE;
2432 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2433 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2435 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2436 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2437 if( msg.message == WM_KEYDOWN &&
2438 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2440 pmt->trackFlags |= TF_SUSPENDPOPUP;
2447 /* failures go through this */
2448 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2452 /***********************************************************************
2455 * Handle a VK_ESCAPE key event in a menu.
2457 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2459 BOOL bEndMenu = TRUE;
2461 if (pmt->hCurrentMenu != pmt->hTopMenu)
2463 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2465 if (menu->wFlags & MF_POPUP)
2467 HMENU hmenutmp, hmenuprev;
2469 hmenuprev = hmenutmp = pmt->hTopMenu;
2471 /* close topmost popup */
2472 while (hmenutmp != pmt->hCurrentMenu)
2474 hmenuprev = hmenutmp;
2475 hmenutmp = MENU_GetSubPopup( hmenuprev );
2478 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2479 pmt->hCurrentMenu = hmenuprev;
2487 /***********************************************************************
2490 * Handle a VK_LEFT key event in a menu.
2492 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2495 HMENU hmenutmp, hmenuprev;
2498 hmenuprev = hmenutmp = pmt->hTopMenu;
2499 menu = MENU_GetMenu( hmenutmp );
2501 /* Try to move 1 column left (if possible) */
2502 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2503 NO_SELECTED_ITEM ) {
2505 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2510 /* close topmost popup */
2511 while (hmenutmp != pmt->hCurrentMenu)
2513 hmenuprev = hmenutmp;
2514 hmenutmp = MENU_GetSubPopup( hmenuprev );
2517 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2518 pmt->hCurrentMenu = hmenuprev;
2520 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2522 /* move menu bar selection if no more popups are left */
2524 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2525 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2527 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2529 /* A sublevel menu was displayed - display the next one
2530 * unless there is another displacement coming up */
2532 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2533 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2534 pmt->hTopMenu, TRUE, wFlags);
2540 /***********************************************************************
2543 * Handle a VK_RIGHT key event in a menu.
2545 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2548 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2551 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2553 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2555 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2557 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2559 /* If already displaying a popup, try to display sub-popup */
2561 hmenutmp = pmt->hCurrentMenu;
2562 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2564 /* if subpopup was displayed then we are done */
2565 if (hmenutmp != pmt->hCurrentMenu) return;
2568 /* Check to see if there's another column */
2569 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2570 NO_SELECTED_ITEM ) {
2571 TRACE("Going to %d.\n", nextcol );
2572 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2577 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2579 if( pmt->hCurrentMenu != pmt->hTopMenu )
2581 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2582 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2583 } else hmenutmp = 0;
2585 /* try to move to the next item */
2586 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2587 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2589 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2590 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2591 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2592 pmt->hTopMenu, TRUE, wFlags);
2596 /***********************************************************************
2599 * Menu tracking code.
2601 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2602 HWND hwnd, const RECT *lprect )
2607 INT executedMenuId = -1;
2609 BOOL enterIdleSent = FALSE;
2612 mt.hCurrentMenu = hmenu;
2613 mt.hTopMenu = hmenu;
2614 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2618 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2619 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2620 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2623 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2625 if (wFlags & TPM_BUTTONDOWN)
2627 /* Get the result in order to start the tracking or not */
2628 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2629 fEndMenu = !fRemove;
2632 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2636 menu = MENU_GetMenu( mt.hCurrentMenu );
2637 if (!menu) /* sometimes happens if I do a window manager close */
2640 /* we have to keep the message in the queue until it's
2641 * clear that menu loop is not over yet. */
2645 if (PeekMessageA( &msg, 0, 0, 0, PM_NOREMOVE ))
2647 if (!CallMsgFilterA( &msg, MSGF_MENU )) break;
2648 /* remove the message from the queue */
2649 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2655 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2656 enterIdleSent = TRUE;
2657 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2663 /* check if EndMenu() tried to cancel us, by posting this message */
2664 if(msg.message == WM_CANCELMODE)
2666 /* we are now out of the loop */
2669 /* remove the message from the queue */
2670 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2672 /* break out of internal loop, ala ESCAPE */
2676 TranslateMessage( &msg );
2679 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2680 enterIdleSent=FALSE;
2683 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2686 * use the mouse coordinates in lParam instead of those in the MSG
2687 * struct to properly handle synthetic messages. lParam coords are
2688 * relative to client area, so they must be converted; since they can
2689 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2691 mt.pt.x = SLOWORD(msg.lParam);
2692 mt.pt.y = SHIWORD(msg.lParam);
2693 ClientToScreen(msg.hwnd,&mt.pt);
2695 /* Find a menu for this mouse event */
2696 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2700 /* no WM_NC... messages in captured state */
2702 case WM_RBUTTONDBLCLK:
2703 case WM_RBUTTONDOWN:
2704 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2706 case WM_LBUTTONDBLCLK:
2707 case WM_LBUTTONDOWN:
2708 /* If the message belongs to the menu, removes it from the queue */
2709 /* Else, end menu tracking */
2710 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2711 fEndMenu = !fRemove;
2715 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2718 /* Check if a menu was selected by the mouse */
2721 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2723 /* End the loop if executedMenuId is an item ID */
2724 /* or if the job was done (executedMenuId = 0). */
2725 fEndMenu = fRemove = (executedMenuId != -1);
2727 /* No menu was selected by the mouse */
2728 /* if the function was called by TrackPopupMenu, continue
2729 with the menu tracking. If not, stop it */
2731 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2736 /* In win95 winelook, the selected menu item must be changed every time the
2737 mouse moves. In Win31 winelook, the mouse button has to be held down */
2739 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2740 ( (msg.wParam & MK_LBUTTON) ||
2741 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2743 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2745 } /* switch(msg.message) - mouse */
2747 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2749 fRemove = TRUE; /* Keyboard messages are always removed */
2757 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2758 NO_SELECTED_ITEM, FALSE, 0 );
2761 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2762 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2765 case VK_DOWN: /* If on menu bar, pull-down the menu */
2767 menu = MENU_GetMenu( mt.hCurrentMenu );
2768 if (!(menu->wFlags & MF_POPUP))
2769 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2770 else /* otherwise try to move selection */
2771 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2775 MENU_KeyLeft( &mt, wFlags );
2779 MENU_KeyRight( &mt, wFlags );
2783 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2789 hi.cbSize = sizeof(HELPINFO);
2790 hi.iContextType = HELPINFO_MENUITEM;
2791 if (menu->FocusedItem == NO_SELECTED_ITEM)
2794 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2795 hi.hItemHandle = hmenu;
2796 hi.dwContextId = menu->dwContextHelpID;
2797 hi.MousePos = msg.pt;
2798 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2805 break; /* WM_KEYDOWN */
2815 break; /* WM_SYSKEYDOWN */
2821 if (msg.wParam == '\r' || msg.wParam == ' ')
2823 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2824 fEndMenu = (executedMenuId != -1);
2829 /* Hack to avoid control chars. */
2830 /* We will find a better way real soon... */
2831 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2833 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2834 LOWORD(msg.wParam), FALSE );
2835 if (pos == (UINT)-2) fEndMenu = TRUE;
2836 else if (pos == (UINT)-1) MessageBeep(0);
2839 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2841 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2842 fEndMenu = (executedMenuId != -1);
2846 } /* switch(msg.message) - kbd */
2850 DispatchMessageA( &msg );
2853 if (!fEndMenu) fRemove = TRUE;
2855 /* finally remove message from the queue */
2857 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2858 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2859 else mt.trackFlags &= ~TF_SKIPREMOVE;
2864 /* If dropdown is still painted and the close box is clicked on
2865 then the menu will be destroyed as part of the DispatchMessage above.
2866 This will then invalidate the menu handle in mt.hTopMenu. We should
2867 check for this first. */
2868 if( IsMenu( mt.hTopMenu ) )
2870 menu = MENU_GetMenu( mt.hTopMenu );
2872 if( IsWindow( mt.hOwnerWnd ) )
2874 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2876 if (menu && menu->wFlags & MF_POPUP)
2878 DestroyWindow( menu->hWnd );
2881 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2882 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2885 /* Reset the variable for hiding menu */
2886 if( menu ) menu->bTimeToHide = FALSE;
2889 /* The return value is only used by TrackPopupMenu */
2890 return ((executedMenuId != -1) ? executedMenuId : 0);
2893 /***********************************************************************
2896 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2898 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
2902 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2903 if (!(wFlags & TPM_NONOTIFY))
2904 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2906 SendMessageA( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2908 if (!(wFlags & TPM_NONOTIFY))
2911 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
2912 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2913 { /* app changed/recreated menu bar entries in WM_INITMENU
2914 Recalculate menu sizes else clicks will not work */
2915 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2916 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2922 /***********************************************************************
2925 static BOOL MENU_ExitTracking(HWND hWnd)
2927 TRACE("hwnd=0x%04x\n", hWnd);
2929 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
2934 /***********************************************************************
2935 * MENU_TrackMouseMenuBar
2937 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2939 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2941 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2942 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2944 TRACE("wnd=%x ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2948 /* map point to parent client coordinates */
2949 HWND parent = GetAncestor( hWnd, GA_PARENT );
2950 if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
2952 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2953 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2954 MENU_ExitTracking(hWnd);
2959 /***********************************************************************
2960 * MENU_TrackKbdMenuBar
2962 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2964 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, INT vkey)
2966 UINT uItem = NO_SELECTED_ITEM;
2968 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2970 /* find window that has a menu */
2972 while (GetWindowLongA( hwnd, GWL_STYLE ) & WS_CHILD)
2973 if (!(hwnd = GetParent( hwnd ))) return;
2975 /* check if we have to track a system menu */
2977 hTrackMenu = GetMenu( hwnd );
2978 if (!hTrackMenu || IsIconic(hwnd) || vkey == VK_SPACE )
2980 if (!(GetWindowLongA( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
2981 hTrackMenu = get_win_sys_menu( hwnd );
2983 wParam |= HTSYSMENU; /* prevent item lookup */
2986 if (!IsMenu( hTrackMenu )) return;
2988 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
2990 if( vkey && vkey != VK_SPACE )
2992 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, vkey, (wParam & HTSYSMENU) );
2993 if( uItem >= (UINT)(-2) )
2995 if( uItem == (UINT)(-1) ) MessageBeep(0);
3002 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3004 if( uItem == NO_SELECTED_ITEM )
3005 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3007 PostMessageA( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3009 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3011 MENU_ExitTracking( hwnd );
3015 /**********************************************************************
3016 * TrackPopupMenu (USER32.@)
3018 * Like the win32 API, the function return the command ID only if the
3019 * flag TPM_RETURNCMD is on.
3022 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3023 INT nReserved, HWND hWnd, const RECT *lpRect )
3027 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3029 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3030 if (!(wFlags & TPM_NONOTIFY))
3031 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3033 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3034 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3035 MENU_ExitTracking(hWnd);
3037 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3043 /**********************************************************************
3044 * TrackPopupMenuEx (USER32.@)
3046 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3047 HWND hWnd, LPTPMPARAMS lpTpm )
3049 FIXME("not fully implemented\n" );
3050 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3051 lpTpm ? &lpTpm->rcExclude : NULL );
3054 /***********************************************************************
3057 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3059 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3061 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3062 hwnd, message, wParam, lParam);
3068 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3069 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3073 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3074 return MA_NOACTIVATE;
3079 BeginPaint( hwnd, &ps );
3080 MENU_DrawPopupMenu( hwnd, ps.hdc,
3081 (HMENU)GetWindowLongA( hwnd, 0 ) );
3082 EndPaint( hwnd, &ps );
3089 /* zero out global pointer in case resident popup window was destroyed. */
3090 if (hwnd == top_popup) top_popup = 0;
3097 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3100 SetWindowLongW( hwnd, 0, 0 );
3103 case MM_SETMENUHANDLE:
3104 SetWindowLongW( hwnd, 0, wParam );
3107 case MM_GETMENUHANDLE:
3108 return GetWindowLongW( hwnd, 0 );
3111 return DefWindowProcW( hwnd, message, wParam, lParam );
3117 /***********************************************************************
3118 * MENU_GetMenuBarHeight
3120 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3122 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3123 INT orgX, INT orgY )
3129 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3130 hwnd, menubarWidth, orgX, orgY );
3132 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3134 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3135 SelectObject( hdc, hMenuFont);
3136 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3137 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3138 ReleaseDC( hwnd, hdc );
3139 return lppop->Height;
3143 /*******************************************************************
3144 * ChangeMenu (USER.153)
3146 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3147 UINT16 id, UINT16 flags )
3149 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3150 hMenu, pos, (DWORD)data, id, flags );
3151 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3154 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3155 /* for MF_DELETE. We should check the parameters for all others */
3156 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3158 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3159 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3161 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3162 flags & MF_BYPOSITION ? pos : id,
3163 flags & ~MF_REMOVE );
3164 /* Default: MF_INSERT */
3165 return InsertMenu16( hMenu, pos, flags, id, data );
3169 /*******************************************************************
3170 * ChangeMenuA (USER32.@)
3172 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3173 UINT id, UINT flags )
3175 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3176 hMenu, pos, (DWORD)data, id, flags );
3177 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3179 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3180 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3182 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3183 flags & MF_BYPOSITION ? pos : id,
3184 flags & ~MF_REMOVE );
3185 /* Default: MF_INSERT */
3186 return InsertMenuA( hMenu, pos, flags, id, data );
3190 /*******************************************************************
3191 * ChangeMenuW (USER32.@)
3193 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3194 UINT id, UINT flags )
3196 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3197 hMenu, pos, (DWORD)data, id, flags );
3198 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3200 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3201 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3203 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3204 flags & MF_BYPOSITION ? pos : id,
3205 flags & ~MF_REMOVE );
3206 /* Default: MF_INSERT */
3207 return InsertMenuW( hMenu, pos, flags, id, data );
3211 /*******************************************************************
3212 * CheckMenuItem (USER.154)
3214 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3216 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3220 /*******************************************************************
3221 * CheckMenuItem (USER32.@)
3223 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3228 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3229 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3230 ret = item->fState & MF_CHECKED;
3231 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3232 else item->fState &= ~MF_CHECKED;
3237 /**********************************************************************
3238 * EnableMenuItem (USER.155)
3240 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3242 return EnableMenuItem( hMenu, wItemID, wFlags );
3246 /**********************************************************************
3247 * EnableMenuItem (USER32.@)
3249 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3255 TRACE("(%04x, %04X, %04X) !\n",
3256 hMenu, wItemID, wFlags);
3258 /* Get the Popupmenu to access the owner menu */
3259 if (!(menu = MENU_GetMenu(hMenu)))
3262 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3265 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3266 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3268 /* In win95 if the close item in the system menu change update the close button */
3269 if (TWEAK_WineLook == WIN95_LOOK)
3270 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3272 if (menu->hSysMenuOwner != 0)
3274 POPUPMENU* parentMenu;
3276 /* Get the parent menu to access*/
3277 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3280 /* Refresh the frame to reflect the change*/
3281 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3282 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3290 /*******************************************************************
3291 * GetMenuString (USER.161)
3293 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3294 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3296 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3300 /*******************************************************************
3301 * GetMenuStringA (USER32.@)
3303 INT WINAPI GetMenuStringA(
3304 HMENU hMenu, /* [in] menuhandle */
3305 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3306 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3307 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3308 UINT wFlags /* [in] MF_ flags */
3312 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3313 hMenu, wItemID, str, nMaxSiz, wFlags );
3314 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3315 if (!IS_STRING_ITEM(item->fType)) return 0;
3316 if (!str || !nMaxSiz) return strlenW(item->text);
3318 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3320 TRACE("returning '%s'\n", str );
3325 /*******************************************************************
3326 * GetMenuStringW (USER32.@)
3328 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3329 LPWSTR str, INT nMaxSiz, UINT wFlags )
3333 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3334 hMenu, wItemID, str, nMaxSiz, wFlags );
3335 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3336 if (!IS_STRING_ITEM(item->fType)) return 0;
3337 if (!str || !nMaxSiz) return strlenW(item->text);
3339 lstrcpynW( str, item->text, nMaxSiz );
3340 return strlenW(str);
3344 /**********************************************************************
3345 * HiliteMenuItem (USER32.@)
3347 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3351 TRACE("(%04x, %04x, %04x, %04x);\n",
3352 hWnd, hMenu, wItemID, wHilite);
3353 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3354 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3355 if (menu->FocusedItem == wItemID) return TRUE;
3356 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3357 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3362 /**********************************************************************
3363 * GetMenuState (USER.250)
3365 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3367 return GetMenuState( hMenu, wItemID, wFlags );
3371 /**********************************************************************
3372 * GetMenuState (USER32.@)
3374 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3377 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3378 hMenu, wItemID, wFlags);
3379 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3380 debug_print_menuitem (" item: ", item, "");
3381 if (item->fType & MF_POPUP)
3383 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3384 if (!menu) return -1;
3385 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3389 /* We used to (from way back then) mask the result to 0xff. */
3390 /* I don't know why and it seems wrong as the documented */
3391 /* return flag MF_SEPARATOR is outside that mask. */
3392 return (item->fType | item->fState);
3397 /**********************************************************************
3398 * GetMenuItemCount (USER.263)
3400 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3402 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3403 if (!menu) return -1;
3404 TRACE("(%04x) returning %d\n",
3405 hMenu, menu->nItems );
3406 return menu->nItems;
3410 /**********************************************************************
3411 * GetMenuItemCount (USER32.@)
3413 INT WINAPI GetMenuItemCount( HMENU hMenu )
3415 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3416 if (!menu) return -1;
3417 TRACE("(%04x) returning %d\n",
3418 hMenu, menu->nItems );
3419 return menu->nItems;
3422 /**********************************************************************
3423 * GetMenuItemID (USER.264)
3425 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3427 return (UINT16) GetMenuItemID (hMenu, nPos);
3430 /**********************************************************************
3431 * GetMenuItemID (USER32.@)
3433 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3437 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3438 if (lpmi->fType & MF_POPUP) return -1;
3443 /*******************************************************************
3444 * InsertMenu (USER.410)
3446 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3447 UINT16 id, SEGPTR data )
3449 UINT pos32 = (UINT)pos;
3450 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3451 if (IS_STRING_ITEM(flags) && data)
3452 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3453 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3457 /*******************************************************************
3458 * InsertMenuW (USER32.@)
3460 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3461 UINT id, LPCWSTR str )
3465 if (IS_STRING_ITEM(flags) && str)
3466 TRACE("hMenu %04x, pos %d, flags %08x, "
3467 "id %04x, str %s\n",
3468 hMenu, pos, flags, id, debugstr_w(str) );
3469 else TRACE("hMenu %04x, pos %d, flags %08x, "
3470 "id %04x, str %08lx (not a string)\n",
3471 hMenu, pos, flags, id, (DWORD)str );
3473 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3475 if (!(MENU_SetItemData( item, flags, id, str )))
3477 RemoveMenu( hMenu, pos, flags );
3481 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3482 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3484 item->hCheckBit = item->hUnCheckBit = 0;
3489 /*******************************************************************
3490 * InsertMenuA (USER32.@)
3492 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3493 UINT id, LPCSTR str )
3497 if (IS_STRING_ITEM(flags) && str)
3499 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3500 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3503 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3504 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3505 HeapFree( GetProcessHeap(), 0, newstr );
3509 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3513 /*******************************************************************
3514 * AppendMenu (USER.411)
3516 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3518 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3522 /*******************************************************************
3523 * AppendMenuA (USER32.@)
3525 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3526 UINT id, LPCSTR data )
3528 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3532 /*******************************************************************
3533 * AppendMenuW (USER32.@)
3535 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3536 UINT id, LPCWSTR data )
3538 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3542 /**********************************************************************
3543 * RemoveMenu (USER.412)
3545 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3547 return RemoveMenu( hMenu, nPos, wFlags );
3551 /**********************************************************************
3552 * RemoveMenu (USER32.@)
3554 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3559 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3560 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3561 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3565 MENU_FreeItemData( item );
3567 if (--menu->nItems == 0)
3569 HeapFree( GetProcessHeap(), 0, menu->items );
3574 while(nPos < menu->nItems)
3580 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3581 menu->nItems * sizeof(MENUITEM) );
3587 /**********************************************************************
3588 * DeleteMenu (USER.413)
3590 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3592 return DeleteMenu( hMenu, nPos, wFlags );
3596 /**********************************************************************
3597 * DeleteMenu (USER32.@)
3599 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3601 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3602 if (!item) return FALSE;
3603 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3604 /* nPos is now the position of the item */
3605 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3610 /*******************************************************************
3611 * ModifyMenu (USER.414)
3613 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3614 UINT16 id, SEGPTR data )
3616 if (IS_STRING_ITEM(flags))
3617 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3618 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3622 /*******************************************************************
3623 * ModifyMenuW (USER32.@)
3625 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3626 UINT id, LPCWSTR str )
3630 if (IS_STRING_ITEM(flags))
3632 TRACE("%04x %d %04x %04x %s\n",
3633 hMenu, pos, flags, id, debugstr_w(str) );
3634 if (!str) return FALSE;
3638 TRACE("%04x %d %04x %04x %08lx\n",
3639 hMenu, pos, flags, id, (DWORD)str );
3642 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3643 return MENU_SetItemData( item, flags, id, str );
3647 /*******************************************************************
3648 * ModifyMenuA (USER32.@)
3650 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3651 UINT id, LPCSTR str )
3655 if (IS_STRING_ITEM(flags) && str)
3657 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3658 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3661 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3662 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3663 HeapFree( GetProcessHeap(), 0, newstr );
3667 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3671 /**********************************************************************
3672 * CreatePopupMenu (USER.415)
3674 HMENU16 WINAPI CreatePopupMenu16(void)
3676 return CreatePopupMenu();
3680 /**********************************************************************
3681 * CreatePopupMenu (USER32.@)
3683 HMENU WINAPI CreatePopupMenu(void)
3688 if (!(hmenu = CreateMenu())) return 0;
3689 menu = MENU_GetMenu( hmenu );
3690 menu->wFlags |= MF_POPUP;
3691 menu->bTimeToHide = FALSE;
3696 /**********************************************************************
3697 * GetMenuCheckMarkDimensions (USER.417)
3698 * GetMenuCheckMarkDimensions (USER32.@)
3700 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3702 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3706 /**********************************************************************
3707 * SetMenuItemBitmaps (USER.418)
3709 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3710 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3712 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3716 /**********************************************************************
3717 * SetMenuItemBitmaps (USER32.@)
3719 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3720 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3723 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3724 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3725 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3727 if (!hNewCheck && !hNewUnCheck)
3729 item->fState &= ~MF_USECHECKBITMAPS;
3731 else /* Install new bitmaps */
3733 item->hCheckBit = hNewCheck;
3734 item->hUnCheckBit = hNewUnCheck;
3735 item->fState |= MF_USECHECKBITMAPS;
3741 /**********************************************************************
3742 * CreateMenu (USER.151)
3744 HMENU16 WINAPI CreateMenu16(void)
3746 return CreateMenu();
3750 /**********************************************************************
3751 * CreateMenu (USER32.@)
3753 HMENU WINAPI CreateMenu(void)
3757 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3758 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3760 ZeroMemory(menu, sizeof(POPUPMENU));
3761 menu->wMagic = MENU_MAGIC;
3762 menu->FocusedItem = NO_SELECTED_ITEM;
3763 menu->bTimeToHide = FALSE;
3765 TRACE("return %04x\n", hMenu );
3771 /**********************************************************************
3772 * DestroyMenu (USER.152)
3774 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3776 return DestroyMenu( hMenu );
3780 /**********************************************************************
3781 * DestroyMenu (USER32.@)
3783 BOOL WINAPI DestroyMenu( HMENU hMenu )
3785 TRACE("(%04x)\n", hMenu);
3787 /* Silently ignore attempts to destroy default system popup */
3789 if (hMenu && hMenu != MENU_DefSysPopup)
3791 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3793 if (!lppop) return FALSE;
3795 lppop->wMagic = 0; /* Mark it as destroyed */
3797 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3799 DestroyWindow( lppop->hWnd );
3803 if (lppop->items) /* recursively destroy submenus */
3806 MENUITEM *item = lppop->items;
3807 for (i = lppop->nItems; i > 0; i--, item++)
3809 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3810 MENU_FreeItemData( item );
3812 HeapFree( GetProcessHeap(), 0, lppop->items );
3814 USER_HEAP_FREE( hMenu );
3816 return (hMenu != MENU_DefSysPopup);
3820 /**********************************************************************
3821 * GetSystemMenu (USER32.@)
3823 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3825 WND *wndPtr = WIN_FindWndPtr( hWnd );
3830 if( wndPtr->hSysMenu )
3834 DestroyMenu(wndPtr->hSysMenu);
3835 wndPtr->hSysMenu = 0;
3839 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3842 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3843 menu->items[0].hSubMenu = MENU_CopySysPopup();
3847 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
3848 wndPtr->hSysMenu, hWnd);
3849 wndPtr->hSysMenu = 0;
3854 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3855 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3857 if( wndPtr->hSysMenu )
3860 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3862 /* Store the dummy sysmenu handle to facilitate the refresh */
3863 /* of the close button if the SC_CLOSE item change */
3864 menu = MENU_GetMenu(retvalue);
3866 menu->hSysMenuOwner = wndPtr->hSysMenu;
3868 WIN_ReleaseWndPtr(wndPtr);
3870 return bRevert ? 0 : retvalue;
3874 /*******************************************************************
3875 * SetSystemMenu (USER32.@)
3877 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3879 WND *wndPtr = WIN_FindWndPtr(hwnd);
3883 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3884 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3885 WIN_ReleaseWndPtr(wndPtr);
3892 /**********************************************************************
3893 * GetMenu (USER32.@)
3895 HMENU WINAPI GetMenu( HWND hWnd )
3897 HMENU retvalue = (HMENU)GetWindowLongA( hWnd, GWL_ID );
3898 TRACE("for %04x returning %04x\n", hWnd, retvalue);
3903 /**********************************************************************
3904 * SetMenu (USER32.@)
3906 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3908 TRACE("(%04x, %04x);\n", hWnd, hMenu);
3910 if (hMenu && !IsMenu(hMenu))
3912 WARN("hMenu %x is not a menu handle\n", hMenu);
3915 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3917 hWnd = WIN_GetFullHandle( hWnd );
3918 if (GetCapture() == hWnd) ReleaseCapture();
3924 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3926 lpmenu->hWnd = hWnd;
3927 lpmenu->Height = 0; /* Make sure we recalculate the size */
3929 SetWindowLongA( hWnd, GWL_ID, hMenu );
3931 if (IsWindowVisible(hWnd))
3932 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3933 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3939 /**********************************************************************
3940 * GetSubMenu (USER.159)
3942 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
3944 return GetSubMenu( hMenu, nPos );
3948 /**********************************************************************
3949 * GetSubMenu (USER32.@)
3951 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3955 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3956 if (!(lpmi->fType & MF_POPUP)) return 0;
3957 return lpmi->hSubMenu;
3961 /**********************************************************************
3962 * DrawMenuBar (USER32.@)
3964 BOOL WINAPI DrawMenuBar( HWND hWnd )
3967 HMENU hMenu = GetMenu(hWnd);
3969 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3970 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3972 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3973 lppop->hwndOwner = hWnd;
3974 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3975 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3979 /***********************************************************************
3980 * DrawMenuBarTemp (USER32.@)
3982 DWORD WINAPI DrawMenuBarTemp(DWORD p1, DWORD p2)
3984 FIXME("(%08lx %08lx): stub\n", p1, p2);
3988 /***********************************************************************
3989 * EndMenu (USER.187)
3990 * EndMenu (USER32.@)
3992 void WINAPI EndMenu(void)
3994 /* if we are in the menu code, and it is active */
3995 if (!fEndMenu && top_popup)
3997 /* terminate the menu handling code */
4000 /* needs to be posted to wakeup the internal menu handler */
4001 /* which will now terminate the menu, in the event that */
4002 /* the main window was minimized, or lost focus, so we */
4003 /* don't end up with an orphaned menu */
4004 PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
4009 /***********************************************************************
4010 * LookupMenuHandle (USER.217)
4012 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4014 HMENU hmenu32 = hmenu;
4016 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4017 else return hmenu32;
4021 /**********************************************************************
4022 * LoadMenu (USER.150)
4024 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4030 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4034 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4037 if (!name) return 0;
4039 /* check for Win32 module */
4040 if (HIWORD(instance)) return LoadMenuA( instance, name );
4041 instance = GetExePtr( instance );
4043 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4044 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4045 hMenu = LoadMenuIndirect16(LockResource16(handle));
4046 FreeResource16( handle );
4051 /*****************************************************************
4052 * LoadMenuA (USER32.@)
4054 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4056 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4057 if (!hrsrc) return 0;
4058 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4062 /*****************************************************************
4063 * LoadMenuW (USER32.@)
4065 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4067 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4068 if (!hrsrc) return 0;
4069 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4073 /**********************************************************************
4074 * LoadMenuIndirect (USER.220)
4076 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4079 WORD version, offset;
4080 LPCSTR p = (LPCSTR)template;
4082 TRACE("(%p)\n", template );
4083 version = GET_WORD(p);
4087 WARN("version must be 0 for Win16\n" );
4090 offset = GET_WORD(p);
4091 p += sizeof(WORD) + offset;
4092 if (!(hMenu = CreateMenu())) return 0;
4093 if (!MENU_ParseResource( p, hMenu, FALSE ))
4095 DestroyMenu( hMenu );
4102 /**********************************************************************
4103 * LoadMenuIndirectA (USER32.@)
4105 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4108 WORD version, offset;
4109 LPCSTR p = (LPCSTR)template;
4111 TRACE("%p\n", template );
4112 version = GET_WORD(p);
4117 offset = GET_WORD(p);
4118 p += sizeof(WORD) + offset;
4119 if (!(hMenu = CreateMenu())) return 0;
4120 if (!MENU_ParseResource( p, hMenu, TRUE ))
4122 DestroyMenu( hMenu );
4127 offset = GET_WORD(p);
4128 p += sizeof(WORD) + offset;
4129 if (!(hMenu = CreateMenu())) return 0;
4130 if (!MENUEX_ParseResource( p, hMenu))
4132 DestroyMenu( hMenu );
4137 ERR("version %d not supported.\n", version);
4143 /**********************************************************************
4144 * LoadMenuIndirectW (USER32.@)
4146 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4148 /* FIXME: is there anything different between A and W? */
4149 return LoadMenuIndirectA( template );
4153 /**********************************************************************
4156 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4158 return IsMenu( hmenu );
4162 /**********************************************************************
4165 BOOL WINAPI IsMenu(HMENU hmenu)
4167 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4168 return menu != NULL;
4171 /**********************************************************************
4172 * GetMenuItemInfo_common
4175 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4176 LPMENUITEMINFOW lpmii, BOOL unicode)
4178 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4180 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4185 if (lpmii->fMask & MIIM_TYPE) {
4186 lpmii->fType = menu->fType;
4187 switch (MENU_ITEM_TYPE(menu->fType)) {
4189 break; /* will be done below */
4192 lpmii->dwTypeData = menu->text;
4199 /* copy the text string */
4200 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4201 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4206 len = strlenW(menu->text);
4207 if(lpmii->dwTypeData && lpmii->cch)
4208 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4212 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4213 if(lpmii->dwTypeData && lpmii->cch)
4214 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4215 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4216 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4218 /* if we've copied a substring we return its length */
4219 if(lpmii->dwTypeData && lpmii->cch)
4221 if (lpmii->cch <= len) lpmii->cch--;
4223 else /* return length of string */
4227 if (lpmii->fMask & MIIM_FTYPE)
4228 lpmii->fType = menu->fType;
4230 if (lpmii->fMask & MIIM_BITMAP)
4231 lpmii->hbmpItem = menu->hbmpItem;
4233 if (lpmii->fMask & MIIM_STATE)
4234 lpmii->fState = menu->fState;
4236 if (lpmii->fMask & MIIM_ID)
4237 lpmii->wID = menu->wID;
4239 if (lpmii->fMask & MIIM_SUBMENU)
4240 lpmii->hSubMenu = menu->hSubMenu;
4242 if (lpmii->fMask & MIIM_CHECKMARKS) {
4243 lpmii->hbmpChecked = menu->hCheckBit;
4244 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4246 if (lpmii->fMask & MIIM_DATA)
4247 lpmii->dwItemData = menu->dwItemData;
4252 /**********************************************************************
4253 * GetMenuItemInfoA (USER32.@)
4255 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4256 LPMENUITEMINFOA lpmii)
4258 return GetMenuItemInfo_common (hmenu, item, bypos,
4259 (LPMENUITEMINFOW)lpmii, FALSE);
4262 /**********************************************************************
4263 * GetMenuItemInfoW (USER32.@)
4265 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4266 LPMENUITEMINFOW lpmii)
4268 return GetMenuItemInfo_common (hmenu, item, bypos,
4273 /* set a menu item text from a ASCII or Unicode string */
4274 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4279 menu->fType |= MF_SEPARATOR;
4283 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4284 strcpyW( menu->text, text );
4288 LPCSTR str = (LPCSTR)text;
4289 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4290 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4291 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4296 /**********************************************************************
4297 * SetMenuItemInfo_common
4300 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4301 const MENUITEMINFOW *lpmii,
4304 if (!menu) return FALSE;
4306 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4308 if (lpmii->fMask & MIIM_TYPE ) {
4309 /* Get rid of old string. */
4310 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4311 HeapFree(GetProcessHeap(), 0, menu->text);
4315 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4316 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4317 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4319 menu->text = lpmii->dwTypeData;
4321 if (IS_STRING_ITEM(menu->fType))
4322 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4325 if (lpmii->fMask & MIIM_FTYPE ) {
4326 /* free the string when the type is changing */
4327 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4328 HeapFree(GetProcessHeap(), 0, menu->text);
4331 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4332 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4333 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4334 menu->fType |= MF_SEPARATOR;
4337 if (lpmii->fMask & MIIM_STRING ) {
4338 /* free the string when used */
4339 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4340 HeapFree(GetProcessHeap(), 0, menu->text);
4341 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4345 if (lpmii->fMask & MIIM_STATE)
4347 /* fixme: MFS_DEFAULT do we have to reset the other menu items? */
4348 menu->fState = lpmii->fState;
4351 if (lpmii->fMask & MIIM_ID)
4352 menu->wID = lpmii->wID;
4354 if (lpmii->fMask & MIIM_SUBMENU) {
4355 menu->hSubMenu = lpmii->hSubMenu;
4356 if (menu->hSubMenu) {
4357 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4359 subMenu->wFlags |= MF_POPUP;
4360 menu->fType |= MF_POPUP;
4363 /* FIXME: Return an error ? */
4364 menu->fType &= ~MF_POPUP;
4367 menu->fType &= ~MF_POPUP;
4370 if (lpmii->fMask & MIIM_CHECKMARKS)
4372 if (lpmii->fType & MFT_RADIOCHECK)
4373 menu->fType |= MFT_RADIOCHECK;
4375 menu->hCheckBit = lpmii->hbmpChecked;
4376 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4378 if (lpmii->fMask & MIIM_DATA)
4379 menu->dwItemData = lpmii->dwItemData;
4381 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4385 /**********************************************************************
4386 * SetMenuItemInfoA (USER32.@)
4388 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4389 const MENUITEMINFOA *lpmii)
4391 if ((lpmii->fType & (MF_HILITE|MF_POPUP)) || (lpmii->fState)) {
4392 /* QuickTime does pass invalid data into SetMenuItemInfo.
4393 * do some of the checks Windows does.
4395 WARN("Bad masks for type (0x%08x) or state (0x%08x)\n",
4396 lpmii->fType,lpmii->fState );
4400 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4401 (const MENUITEMINFOW *)lpmii, FALSE);
4404 /**********************************************************************
4405 * SetMenuItemInfoW (USER32.@)
4407 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4408 const MENUITEMINFOW *lpmii)
4410 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4414 /**********************************************************************
4415 * SetMenuDefaultItem (USER32.@)
4418 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4424 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4426 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4428 /* reset all default-item flags */
4430 for (i = 0; i < menu->nItems; i++, item++)
4432 item->fState &= ~MFS_DEFAULT;
4435 /* no default item */
4444 if ( uItem >= menu->nItems ) return FALSE;
4445 item[uItem].fState |= MFS_DEFAULT;
4450 for (i = 0; i < menu->nItems; i++, item++)
4452 if (item->wID == uItem)
4454 item->fState |= MFS_DEFAULT;
4463 /**********************************************************************
4464 * GetMenuDefaultItem (USER32.@)
4466 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4472 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4474 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4476 /* find default item */
4480 if (! item) return -1;
4482 while ( !( item->fState & MFS_DEFAULT ) )
4485 if (i >= menu->nItems ) return -1;
4488 /* default: don't return disabled items */
4489 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4491 /* search rekursiv when needed */
4492 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4495 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4496 if ( -1 != ret ) return ret;
4498 /* when item not found in submenu, return the popup item */
4500 return ( bypos ) ? i : item->wID;
4504 /*******************************************************************
4505 * InsertMenuItem (USER.441)
4509 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4510 const MENUITEMINFO16 *mii )
4514 miia.cbSize = sizeof(miia);
4515 miia.fMask = mii->fMask;
4516 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4517 miia.fType = mii->fType;
4518 miia.fState = mii->fState;
4519 miia.wID = mii->wID;
4520 miia.hSubMenu = mii->hSubMenu;
4521 miia.hbmpChecked = mii->hbmpChecked;
4522 miia.hbmpUnchecked = mii->hbmpUnchecked;
4523 miia.dwItemData = mii->dwItemData;
4524 miia.cch = mii->cch;
4525 if (IS_STRING_ITEM(miia.fType))
4526 miia.dwTypeData = MapSL(mii->dwTypeData);
4527 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4531 /**********************************************************************
4532 * InsertMenuItemA (USER32.@)
4534 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4535 const MENUITEMINFOA *lpmii)
4537 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4538 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4542 /**********************************************************************
4543 * InsertMenuItemW (USER32.@)
4545 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4546 const MENUITEMINFOW *lpmii)
4548 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4549 return SetMenuItemInfo_common(item, lpmii, TRUE);
4552 /**********************************************************************
4553 * CheckMenuRadioItem (USER32.@)
4556 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4557 UINT first, UINT last, UINT check,
4560 MENUITEM *mifirst, *milast, *micheck;
4561 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4563 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4564 hMenu, first, last, check, bypos);
4566 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4567 milast = MENU_FindItem (&mlast, &last, bypos);
4568 micheck = MENU_FindItem (&mcheck, &check, bypos);
4570 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4571 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4572 micheck > milast || micheck < mifirst)
4575 while (mifirst <= milast)
4577 if (mifirst == micheck)
4579 mifirst->fType |= MFT_RADIOCHECK;
4580 mifirst->fState |= MFS_CHECKED;
4582 mifirst->fType &= ~MFT_RADIOCHECK;
4583 mifirst->fState &= ~MFS_CHECKED;
4591 /**********************************************************************
4592 * CheckMenuRadioItem (USER.666)
4594 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4595 UINT16 first, UINT16 last, UINT16 check,
4598 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4601 /**********************************************************************
4602 * GetMenuItemRect (USER32.@)
4604 * ATTENTION: Here, the returned values in rect are the screen
4605 * coordinates of the item just like if the menu was
4606 * always on the upper left side of the application.
4609 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4612 POPUPMENU *itemMenu;
4616 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4618 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4619 referenceHwnd = hwnd;
4623 itemMenu = MENU_GetMenu(hMenu);
4624 if (itemMenu == NULL)
4627 if(itemMenu->hWnd == 0)
4629 referenceHwnd = itemMenu->hWnd;
4632 if ((rect == NULL) || (item == NULL))
4637 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4643 /**********************************************************************
4644 * SetMenuInfo (USER32.@)
4647 * MIM_APPLYTOSUBMENUS
4648 * actually use the items to draw the menu
4650 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4654 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4656 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4659 if (lpmi->fMask & MIM_BACKGROUND)
4660 menu->hbrBack = lpmi->hbrBack;
4662 if (lpmi->fMask & MIM_HELPID)
4663 menu->dwContextHelpID = lpmi->dwContextHelpID;
4665 if (lpmi->fMask & MIM_MAXHEIGHT)
4666 menu->cyMax = lpmi->cyMax;
4668 if (lpmi->fMask & MIM_MENUDATA)
4669 menu->dwMenuData = lpmi->dwMenuData;
4671 if (lpmi->fMask & MIM_STYLE)
4672 menu->dwStyle = lpmi->dwStyle;
4679 /**********************************************************************
4680 * GetMenuInfo (USER32.@)
4686 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4689 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4691 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4694 if (lpmi->fMask & MIM_BACKGROUND)
4695 lpmi->hbrBack = menu->hbrBack;
4697 if (lpmi->fMask & MIM_HELPID)
4698 lpmi->dwContextHelpID = menu->dwContextHelpID;
4700 if (lpmi->fMask & MIM_MAXHEIGHT)
4701 lpmi->cyMax = menu->cyMax;
4703 if (lpmi->fMask & MIM_MENUDATA)
4704 lpmi->dwMenuData = menu->dwMenuData;
4706 if (lpmi->fMask & MIM_STYLE)
4707 lpmi->dwStyle = menu->dwStyle;
4714 /**********************************************************************
4715 * SetMenuContextHelpId (USER.384)
4717 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4719 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4723 /**********************************************************************
4724 * SetMenuContextHelpId (USER32.@)
4726 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4730 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4732 if ((menu = MENU_GetMenu(hMenu)))
4734 menu->dwContextHelpID = dwContextHelpID;
4740 /**********************************************************************
4741 * GetMenuContextHelpId (USER.385)
4743 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4745 return GetMenuContextHelpId( hMenu );
4748 /**********************************************************************
4749 * GetMenuContextHelpId (USER32.@)
4751 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4755 TRACE("(0x%04x)\n", hMenu);
4757 if ((menu = MENU_GetMenu(hMenu)))
4759 return menu->dwContextHelpID;
4764 /**********************************************************************
4765 * MenuItemFromPoint (USER32.@)
4767 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4769 FIXME("(0x%04x,0x%04x,(%ld,%ld)):stub\n",
4770 hWnd, hMenu, ptScreen.x, ptScreen.y);
4775 /**********************************************************************
4776 * translate_accelerator
4778 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4779 BYTE fVirt, WORD key, WORD cmd )
4783 if (wParam != key) return FALSE;
4785 if (message == WM_CHAR)
4787 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4789 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4795 if(fVirt & FVIRTKEY)
4798 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4799 wParam, 0xff & HIWORD(lParam));
4800 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4801 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4802 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4803 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4804 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4808 if (!(lParam & 0x01000000)) /* no special_key */
4810 if ((fVirt & FALT) && (lParam & 0x20000000))
4811 { /* ^^ ALT pressed */
4812 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4821 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4823 else if (GetCapture())
4825 else if (!IsWindowEnabled(hWnd))
4829 HMENU hMenu, hSubMenu, hSysMenu;
4830 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4832 hMenu = (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4833 hSysMenu = get_win_sys_menu( hWnd );
4835 /* find menu item and ask application to initialize it */
4836 /* 1. in the system menu */
4837 hSubMenu = hSysMenu;
4839 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4841 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4842 if(hSubMenu != hSysMenu)
4844 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4845 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4846 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4848 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4850 else /* 2. in the window's menu */
4854 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4856 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4857 if(hSubMenu != hMenu)
4859 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4860 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
4861 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4863 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4867 if (uSysStat != (UINT)-1)
4869 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4876 if (uStat != (UINT)-1)
4882 if (uStat & (MF_DISABLED|MF_GRAYED))
4893 if( mesg==WM_COMMAND )
4895 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4896 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
4898 else if( mesg==WM_SYSCOMMAND )
4900 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4901 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
4905 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4906 * #0: unknown (please report!)
4907 * #1: for WM_KEYUP,WM_SYSKEYUP
4908 * #2: mouse is captured
4909 * #3: window is disabled
4910 * #4: it's a disabled system menu option
4911 * #5: it's a menu option, but window is iconic
4912 * #6: it's a menu option, but disabled
4914 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4916 ERR_(accel)(" unknown reason - please report!");
4921 /**********************************************************************
4922 * TranslateAccelerator (USER32.@)
4923 * TranslateAcceleratorA (USER32.@)
4924 * TranslateAcceleratorW (USER32.@)
4926 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
4929 LPACCEL16 lpAccelTbl;
4934 WARN_(accel)("msg null; should hang here to be win compatible\n");
4937 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
4939 WARN_(accel)("invalid accel handle=%x\n", hAccel);
4942 if ((msg->message != WM_KEYDOWN &&
4943 msg->message != WM_KEYUP &&
4944 msg->message != WM_SYSKEYDOWN &&
4945 msg->message != WM_SYSKEYUP &&
4946 msg->message != WM_CHAR)) return 0;
4948 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
4949 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4950 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4955 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4956 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4958 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4959 WARN_(accel)("couldn't translate accelerator key\n");