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.
23 #include "wine/winbase16.h"
24 #include "wine/winuser16.h"
25 #include "wine/unicode.h"
26 #include "wine/port.h"
30 #include "nonclient.h"
34 #include "debugtools.h"
36 DEFAULT_DEBUG_CHANNEL(menu);
37 DECLARE_DEBUG_CHANNEL(accel);
39 /* internal popup menu window messages */
41 #define MM_SETMENUHANDLE (WM_USER + 0)
42 #define MM_GETMENUHANDLE (WM_USER + 1)
44 /* Menu item structure */
46 /* ----------- MENUITEMINFO Stuff ----------- */
47 UINT fType; /* Item type. */
48 UINT fState; /* Item state. */
49 UINT wID; /* Item id. */
50 HMENU hSubMenu; /* Pop-up menu. */
51 HBITMAP hCheckBit; /* Bitmap when checked. */
52 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
53 LPWSTR text; /* Item text or bitmap handle. */
54 DWORD dwItemData; /* Application defined. */
55 DWORD dwTypeData; /* depends on fMask */
56 HBITMAP hbmpItem; /* bitmap in win98 style menus */
57 /* ----------- Wine stuff ----------- */
58 RECT rect; /* Item area (relative to menu window) */
59 UINT xTab; /* X position of text after Tab */
62 /* Popup menu structure */
64 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
65 WORD wMagic; /* Magic number */
66 WORD Width; /* Width of the whole menu */
67 WORD Height; /* Height of the whole menu */
68 UINT nItems; /* Number of items in the menu */
69 HWND hWnd; /* Window containing the menu */
70 MENUITEM *items; /* Array of menu items */
71 UINT FocusedItem; /* Currently focused item */
72 HWND hwndOwner; /* window receiving the messages for ownerdraw */
73 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
74 /* ------------ MENUINFO members ------ */
75 DWORD dwStyle; /* Extended mennu style */
76 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
77 HBRUSH hbrBack; /* brush for menu background */
78 DWORD dwContextHelpID;
79 DWORD dwMenuData; /* application defined value */
80 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
81 } POPUPMENU, *LPPOPUPMENU;
83 /* internal flags for menu tracking */
85 #define TF_ENDMENU 0x0001
86 #define TF_SUSPENDPOPUP 0x0002
87 #define TF_SKIPREMOVE 0x0004
92 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
93 HMENU hTopMenu; /* initial menu */
94 HWND hOwnerWnd; /* where notifications are sent */
98 #define MENU_MAGIC 0x554d /* 'MU' */
103 /* Internal MENU_TrackMenu() flags */
104 #define TPM_INTERNAL 0xF0000000
105 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
106 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
107 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
109 /* popup menu shade thickness */
110 #define POPUP_XSHADE 4
111 #define POPUP_YSHADE 4
113 /* Space between 2 menu bar items */
114 #define MENU_BAR_ITEMS_SPACE 12
116 /* Minimum width of a tab character */
117 #define MENU_TAB_SPACE 8
119 /* Height of a separator item */
120 #define SEPARATOR_HEIGHT 5
122 /* (other menu->FocusedItem values give the position of the focused item) */
123 #define NO_SELECTED_ITEM 0xffff
125 #define MENU_ITEM_TYPE(flags) \
126 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
128 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
129 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
130 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
132 #define IS_SYSTEM_MENU(menu) \
133 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
135 #define IS_SYSTEM_POPUP(menu) \
136 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
138 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
139 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
140 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
141 MF_POPUP | MF_SYSMENU | MF_HELP)
142 #define STATE_MASK (~TYPE_MASK)
144 /* Dimension of the menu bitmaps */
145 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
147 static HBITMAP hStdMnArrow = 0;
149 /* Minimze/restore/close buttons to be inserted in menubar */
150 static HBITMAP hBmpMinimize = 0;
151 static HBITMAP hBmpMinimizeD = 0;
152 static HBITMAP hBmpMaximize = 0;
153 static HBITMAP hBmpMaximizeD = 0;
154 static HBITMAP hBmpClose = 0;
155 static HBITMAP hBmpCloseD = 0;
158 static HBRUSH hShadeBrush = 0;
159 static HFONT hMenuFont = 0;
160 static HFONT hMenuFontBold = 0;
162 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
164 /* Use global popup window because there's no way 2 menus can
165 * be tracked at the same time. */
166 static HWND top_popup;
168 /* Flag set by EndMenu() to force an exit from menu tracking */
169 static BOOL fEndMenu = FALSE;
171 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
174 /*********************************************************************
175 * menu class descriptor
177 const struct builtin_class_descr MENU_builtin_class =
179 POPUPMENU_CLASS_ATOM, /* name */
180 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
181 NULL, /* procA (winproc is Unicode only) */
182 PopupMenuWndProc, /* procW */
183 sizeof(HMENU), /* extra */
184 IDC_ARROWA, /* cursor */
185 COLOR_MENU+1 /* brush */
189 /***********************************************************************
190 * debug_print_menuitem
192 * Print a menuitem in readable form.
195 #define debug_print_menuitem(pre, mp, post) \
196 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
198 #define MENUOUT(text) \
199 DPRINTF("%s%s", (count++ ? "," : ""), (text))
201 #define MENUFLAG(bit,text) \
203 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
206 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
209 TRACE("%s ", prefix);
211 UINT flags = mp->fType;
212 int typ = MENU_ITEM_TYPE(flags);
213 DPRINTF( "{ ID=0x%x", mp->wID);
214 if (flags & MF_POPUP)
215 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
219 if (typ == MFT_STRING)
221 else if (typ == MFT_SEPARATOR)
223 else if (typ == MFT_OWNERDRAW)
225 else if (typ == MFT_BITMAP)
231 MENUFLAG(MF_POPUP, "pop");
232 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
233 MENUFLAG(MFT_MENUBREAK, "brk");
234 MENUFLAG(MFT_RADIOCHECK, "radio");
235 MENUFLAG(MFT_RIGHTORDER, "rorder");
236 MENUFLAG(MF_SYSMENU, "sys");
237 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
240 DPRINTF( "+0x%x", flags);
245 DPRINTF( ", State=");
246 MENUFLAG(MFS_GRAYED, "grey");
247 MENUFLAG(MFS_DEFAULT, "default");
248 MENUFLAG(MFS_DISABLED, "dis");
249 MENUFLAG(MFS_CHECKED, "check");
250 MENUFLAG(MFS_HILITE, "hi");
251 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
252 MENUFLAG(MF_MOUSESELECT, "mouse");
254 DPRINTF( "+0x%x", flags);
257 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
259 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
261 if (typ == MFT_STRING) {
263 DPRINTF( ", Text=%s", debugstr_w(mp->text));
265 DPRINTF( ", Text=Null");
266 } else if (mp->text == NULL)
269 DPRINTF( ", Text=%p", mp->text);
271 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
277 DPRINTF(" %s\n", postfix);
284 /***********************************************************************
287 * Validate the given menu handle and returns the menu structure pointer.
289 POPUPMENU *MENU_GetMenu(HMENU hMenu)
291 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
292 if (!menu || menu->wMagic != MENU_MAGIC)
294 WARN("invalid menu handle=%x, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
300 /***********************************************************************
303 * Return the default system menu.
305 static HMENU MENU_CopySysPopup(void)
307 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
310 POPUPMENU* menu = MENU_GetMenu(hMenu);
311 menu->wFlags |= MF_SYSMENU | MF_POPUP;
312 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
315 ERR("Unable to load default system menu\n" );
317 TRACE("returning %x.\n", hMenu );
323 /**********************************************************************
326 * Create a copy of the system menu. System menu in Windows is
327 * a special menu bar with the single entry - system menu popup.
328 * This popup is presented to the outside world as a "system menu".
329 * However, the real system menu handle is sometimes seen in the
330 * WM_MENUSELECT parameters (and Word 6 likes it this way).
332 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
336 if ((hMenu = CreateMenu()))
338 POPUPMENU *menu = MENU_GetMenu(hMenu);
339 menu->wFlags = MF_SYSMENU;
342 if (hPopupMenu == (HMENU)(-1))
343 hPopupMenu = MENU_CopySysPopup();
344 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
348 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
350 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
351 menu->items[0].fState = 0;
352 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
354 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
357 DestroyMenu( hMenu );
359 ERR("failed to load system menu!\n");
364 /***********************************************************************
367 * Menus initialisation.
372 NONCLIENTMETRICSA ncm;
374 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
379 /* Load menu bitmaps */
380 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
381 /* Load system buttons bitmaps */
382 hBmpMinimize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCE));
383 hBmpMinimizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCED));
384 hBmpMaximize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORE));
385 hBmpMaximizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORED));
386 hBmpClose = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSE));
387 hBmpCloseD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSED));
392 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
393 arrow_bitmap_width = bm.bmWidth;
394 arrow_bitmap_height = bm.bmHeight;
398 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
401 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
404 DeleteObject( hBitmap );
405 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
408 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
409 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
412 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
415 ncm.lfMenuFont.lfWeight += 300;
416 if ( ncm.lfMenuFont.lfWeight > 1000)
417 ncm.lfMenuFont.lfWeight = 1000;
419 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
425 /***********************************************************************
426 * MENU_InitSysMenuPopup
428 * Grey the appropriate items in System menu.
430 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
434 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
435 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
436 gray = ((style & WS_MAXIMIZE) != 0);
437 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
438 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
439 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
440 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
441 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
442 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
443 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
444 gray = (clsStyle & CS_NOCLOSE) != 0;
446 /* The menu item must keep its state if it's disabled */
448 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
452 /******************************************************************************
454 * UINT MENU_GetStartOfNextColumn(
457 *****************************************************************************/
459 static UINT MENU_GetStartOfNextColumn(
462 POPUPMENU *menu = MENU_GetMenu(hMenu);
466 return NO_SELECTED_ITEM;
468 i = menu->FocusedItem + 1;
469 if( i == NO_SELECTED_ITEM )
472 for( ; i < menu->nItems; ++i ) {
473 if (menu->items[i].fType & MF_MENUBARBREAK)
477 return NO_SELECTED_ITEM;
481 /******************************************************************************
483 * UINT MENU_GetStartOfPrevColumn(
486 *****************************************************************************/
488 static UINT MENU_GetStartOfPrevColumn(
491 POPUPMENU *menu = MENU_GetMenu(hMenu);
495 return NO_SELECTED_ITEM;
497 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
498 return NO_SELECTED_ITEM;
500 /* Find the start of the column */
502 for(i = menu->FocusedItem; i != 0 &&
503 !(menu->items[i].fType & MF_MENUBARBREAK);
507 return NO_SELECTED_ITEM;
509 for(--i; i != 0; --i) {
510 if (menu->items[i].fType & MF_MENUBARBREAK)
514 TRACE("ret %d.\n", i );
521 /***********************************************************************
524 * Find a menu item. Return a pointer on the item, and modifies *hmenu
525 * in case the item was in a sub-menu.
527 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
532 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
533 if (wFlags & MF_BYPOSITION)
535 if (*nPos >= menu->nItems) return NULL;
536 return &menu->items[*nPos];
540 MENUITEM *item = menu->items;
541 for (i = 0; i < menu->nItems; i++, item++)
543 if (item->wID == *nPos)
548 else if (item->fType & MF_POPUP)
550 HMENU hsubmenu = item->hSubMenu;
551 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
563 /***********************************************************************
566 * Find a Sub menu. Return the position of the submenu, and modifies
567 * *hmenu in case it is found in another sub-menu.
568 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
570 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
575 if (((*hmenu)==0xffff) ||
576 (!(menu = MENU_GetMenu(*hmenu))))
577 return NO_SELECTED_ITEM;
579 for (i = 0; i < menu->nItems; i++, item++) {
580 if(!(item->fType & MF_POPUP)) continue;
581 if (item->hSubMenu == hSubTarget) {
585 HMENU hsubmenu = item->hSubMenu;
586 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
587 if (pos != NO_SELECTED_ITEM) {
593 return NO_SELECTED_ITEM;
596 /***********************************************************************
599 static void MENU_FreeItemData( MENUITEM* item )
602 if (IS_STRING_ITEM(item->fType) && item->text)
603 HeapFree( GetProcessHeap(), 0, item->text );
606 /***********************************************************************
607 * MENU_FindItemByCoords
609 * Find the item at the specified coordinates (screen coords). Does
610 * not work for child windows and therefore should not be called for
611 * an arbitrary system menu.
613 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
614 POINT pt, UINT *pos )
620 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
621 pt.x -= wrect.left;pt.y -= wrect.top;
623 for (i = 0; i < menu->nItems; i++, item++)
625 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
626 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
636 /***********************************************************************
639 * Find the menu item selected by a key press.
640 * Return item id, -1 if none, -2 if we should close the menu.
642 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
643 UINT key, BOOL forceMenuChar )
645 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
647 if (!IsMenu( hmenu ))
649 WND* w = WIN_FindWndPtr(hwndOwner);
650 hmenu = GetSubMenu(w->hSysMenu, 0);
651 WIN_ReleaseWndPtr(w);
656 POPUPMENU *menu = MENU_GetMenu( hmenu );
657 MENUITEM *item = menu->items;
665 for (i = 0; i < menu->nItems; i++, item++)
667 if (item->text && (IS_STRING_ITEM(item->fType)))
669 WCHAR *p = item->text - 2;
672 p = strchrW (p + 2, '&');
674 while (p != NULL && p [1] == '&');
675 if (p && (toupper(p[1]) == key)) return i;
679 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
680 MAKEWPARAM( key, menu->wFlags ), hmenu );
681 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
682 if (HIWORD(menuchar) == 1) return (UINT)(-2);
686 /***********************************************************************
689 * Load the bitmap associated with the magic menu item and its style
692 static HBITMAP MENU_LoadMagicItem(UINT id, BOOL hilite, DWORD dwItemData)
695 * Magic menu item id's section
696 * These magic id's are used by windows to insert "standard" mdi
697 * buttons (minimize,restore,close) on menu. Under windows,
698 * these magic id's make sure the right things appear when those
699 * bitmap buttons are pressed/selected/released.
703 { case HBMMENU_SYSTEM:
704 return (dwItemData) ?
705 (HBITMAP)dwItemData :
706 (hilite ? hBmpMinimizeD : hBmpMinimize);
707 case HBMMENU_MBAR_RESTORE:
708 return (hilite ? hBmpMaximizeD: hBmpMaximize);
709 case HBMMENU_MBAR_MINIMIZE:
710 return (hilite ? hBmpMinimizeD : hBmpMinimize);
711 case HBMMENU_MBAR_CLOSE:
712 return (hilite ? hBmpCloseD : hBmpClose);
713 case HBMMENU_CALLBACK:
714 case HBMMENU_MBAR_CLOSE_D:
715 case HBMMENU_MBAR_MINIMIZE_D:
716 case HBMMENU_POPUP_CLOSE:
717 case HBMMENU_POPUP_RESTORE:
718 case HBMMENU_POPUP_MAXIMIZE:
719 case HBMMENU_POPUP_MINIMIZE:
721 FIXME("Magic 0x%08x not implemented\n", id);
727 /***********************************************************************
730 * Calculate the size of the menu item and store it in lpitem->rect.
732 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
733 INT orgX, INT orgY, BOOL menuBar )
736 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
738 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
739 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
740 (menuBar ? " (MenuBar)" : ""));
742 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
744 if (lpitem->fType & MF_OWNERDRAW)
747 ** Experimentation under Windows reveals that an owner-drawn
748 ** menu is expected to return the size of the content part of
749 ** the menu item, not including the checkmark nor the submenu
750 ** arrow. Windows adds those values itself and returns the
751 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
753 MEASUREITEMSTRUCT mis;
754 mis.CtlType = ODT_MENU;
756 mis.itemID = lpitem->wID;
757 mis.itemData = (DWORD)lpitem->dwItemData;
760 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
761 lpitem->rect.right += mis.itemWidth;
765 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
768 /* under at least win95 you seem to be given a standard
769 height for the menu and the height value is ignored */
771 if (TWEAK_WineLook == WIN31_LOOK)
772 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
774 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
777 lpitem->rect.bottom += mis.itemHeight;
779 TRACE("id=%04x size=%dx%d\n",
780 lpitem->wID, mis.itemWidth, mis.itemHeight);
781 /* Fall through to get check/arrow width calculation. */
784 if (lpitem->fType & MF_SEPARATOR)
786 lpitem->rect.bottom += SEPARATOR_HEIGHT;
792 lpitem->rect.right += 2 * check_bitmap_width;
793 if (lpitem->fType & MF_POPUP)
794 lpitem->rect.right += arrow_bitmap_width;
797 if (lpitem->fType & MF_OWNERDRAW)
800 if (IS_BITMAP_ITEM(lpitem->fType))
805 /* Check if there is a magic menu item associated with this item */
806 if (IS_MAGIC_ITEM(lpitem->text))
808 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fType & MF_HILITE),
812 resBmp = (HBITMAP)lpitem->text;
814 if (GetObjectA(resBmp, sizeof(bm), &bm ))
816 lpitem->rect.right += bm.bmWidth;
817 lpitem->rect.bottom += bm.bmHeight;
818 if (TWEAK_WineLook == WIN98_LOOK) {
819 /* Leave space for the sunken border */
820 lpitem->rect.right += 2;
821 lpitem->rect.bottom += 2;
828 /* it must be a text item - unless it's the system menu */
829 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
832 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
834 lpitem->rect.right += size.cx;
835 if (TWEAK_WineLook == WIN31_LOOK)
836 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
838 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
843 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
845 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
847 /* Item contains a tab (only meaningful in popup menus) */
848 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
849 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
850 lpitem->rect.right += MENU_TAB_SPACE;
854 if (strchrW( lpitem->text, '\b' ))
855 lpitem->rect.right += MENU_TAB_SPACE;
856 lpitem->xTab = lpitem->rect.right - check_bitmap_width
857 - arrow_bitmap_width;
860 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
864 /***********************************************************************
865 * MENU_PopupMenuCalcSize
867 * Calculate the size of a popup menu.
869 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
874 int orgX, orgY, maxX, maxTab, maxTabWidth;
876 lppop->Width = lppop->Height = 0;
877 if (lppop->nItems == 0) return;
880 SelectObject( hdc, hMenuFont);
883 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
885 while (start < lppop->nItems)
887 lpitem = &lppop->items[start];
889 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
891 maxTab = maxTabWidth = 0;
893 /* Parse items until column break or end of menu */
894 for (i = start; i < lppop->nItems; i++, lpitem++)
897 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
899 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
901 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
902 maxX = max( maxX, lpitem->rect.right );
903 orgY = lpitem->rect.bottom;
904 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
906 maxTab = max( maxTab, lpitem->xTab );
907 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
911 /* Finish the column (set all items to the largest width found) */
912 maxX = max( maxX, maxTab + maxTabWidth );
913 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
915 lpitem->rect.right = maxX;
916 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
917 lpitem->xTab = maxTab;
920 lppop->Height = max( lppop->Height, orgY );
925 /* space for 3d border */
926 if(TWEAK_WineLook > WIN31_LOOK)
936 /***********************************************************************
937 * MENU_MenuBarCalcSize
939 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
940 * height is off by 1 pixel which causes lengthy window relocations when
941 * active document window is maximized/restored.
943 * Calculate the size of the menu bar.
945 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
946 LPPOPUPMENU lppop, HWND hwndOwner )
949 int start, i, orgX, orgY, maxY, helpPos;
951 if ((lprect == NULL) || (lppop == NULL)) return;
952 if (lppop->nItems == 0) return;
953 TRACE("left=%d top=%d right=%d bottom=%d\n",
954 lprect->left, lprect->top, lprect->right, lprect->bottom);
955 lppop->Width = lprect->right - lprect->left;
957 maxY = lprect->top+1;
960 while (start < lppop->nItems)
962 lpitem = &lppop->items[start];
966 /* Parse items until line break or end of menu */
967 for (i = start; i < lppop->nItems; i++, lpitem++)
969 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
971 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
973 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
975 debug_print_menuitem (" item: ", lpitem, "");
976 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
978 if (lpitem->rect.right > lprect->right)
980 if (i != start) break;
981 else lpitem->rect.right = lprect->right;
983 maxY = max( maxY, lpitem->rect.bottom );
984 orgX = lpitem->rect.right;
987 /* Finish the line (set all items to the largest height found) */
988 while (start < i) lppop->items[start++].rect.bottom = maxY;
991 lprect->bottom = maxY;
992 lppop->Height = lprect->bottom - lprect->top;
994 /* Flush right all items between the MF_RIGHTJUSTIFY and */
995 /* the last item (if several lines, only move the last line) */
996 lpitem = &lppop->items[lppop->nItems-1];
997 orgY = lpitem->rect.top;
998 orgX = lprect->right;
999 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1000 if ( (helpPos==-1) || (helpPos>i) )
1002 if (lpitem->rect.top != orgY) break; /* Other line */
1003 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1004 lpitem->rect.left += orgX - lpitem->rect.right;
1005 lpitem->rect.right = orgX;
1006 orgX = lpitem->rect.left;
1010 /***********************************************************************
1013 * Draw a single menu item.
1015 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1016 UINT height, BOOL menuBar, UINT odaction )
1020 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1022 if (lpitem->fType & MF_SYSMENU)
1024 if( !IsIconic(hwnd) ) {
1025 if (TWEAK_WineLook > WIN31_LOOK)
1026 NC_DrawSysButton95( hwnd, hdc,
1028 (MF_HILITE | MF_MOUSESELECT) );
1030 NC_DrawSysButton( hwnd, hdc,
1032 (MF_HILITE | MF_MOUSESELECT) );
1038 if (lpitem->fType & MF_OWNERDRAW)
1041 ** Experimentation under Windows reveals that an owner-drawn
1042 ** menu is given the rectangle which includes the space it requested
1043 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1044 ** and a popup-menu arrow. This is the value of lpitem->rect.
1045 ** Windows will leave all drawing to the application except for
1046 ** the popup-menu arrow. Windows always draws that itself, after
1047 ** the menu owner has finished drawing.
1051 dis.CtlType = ODT_MENU;
1053 dis.itemID = lpitem->wID;
1054 dis.itemData = (DWORD)lpitem->dwItemData;
1056 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1057 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED;
1058 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1059 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1060 dis.hwndItem = hmenu;
1062 dis.rcItem = lpitem->rect;
1063 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1064 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1065 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1066 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1068 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1069 /* Fall through to draw popup-menu arrow */
1072 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1073 lpitem->rect.right,lpitem->rect.bottom);
1075 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1077 rect = lpitem->rect;
1079 if (!(lpitem->fType & MF_OWNERDRAW))
1081 if (lpitem->fState & MF_HILITE)
1083 if(TWEAK_WineLook == WIN98_LOOK)
1086 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1088 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1090 else /* Not Win98 Look */
1092 if(!IS_BITMAP_ITEM(lpitem->fType))
1093 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1097 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1100 SetBkMode( hdc, TRANSPARENT );
1102 if (!(lpitem->fType & MF_OWNERDRAW))
1104 /* vertical separator */
1105 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1107 if (TWEAK_WineLook > WIN31_LOOK)
1111 rc.bottom = height - 3;
1112 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1116 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1117 MoveToEx( hdc, rect.left, 0, NULL );
1118 LineTo( hdc, rect.left, height );
1122 /* horizontal separator */
1123 if (lpitem->fType & MF_SEPARATOR)
1125 if (TWEAK_WineLook > WIN31_LOOK)
1130 rc.top += SEPARATOR_HEIGHT / 2;
1131 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1135 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1136 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1137 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1145 if (lpitem->fState & MF_HILITE)
1147 if(TWEAK_WineLook == WIN98_LOOK)
1150 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1151 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1153 if(lpitem->fState & MF_GRAYED)
1154 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1156 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1157 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1160 else /* Not Win98 Look */
1162 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1163 if(!IS_BITMAP_ITEM(lpitem->fType))
1164 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1169 if (lpitem->fState & MF_GRAYED)
1170 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1172 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1173 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1176 /* helper lines for debugging */
1177 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1178 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1179 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1180 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1185 INT y = rect.top + rect.bottom;
1186 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1187 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1189 if (!(lpitem->fType & MF_OWNERDRAW))
1191 /* Draw the check mark
1194 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1196 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1197 if (bm) /* we have a custom bitmap */
1199 HDC hdcMem = CreateCompatibleDC( hdc );
1200 SelectObject( hdcMem, bm );
1201 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1202 check_bitmap_width, check_bitmap_height,
1203 hdcMem, 0, 0, SRCCOPY );
1206 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1209 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1210 HDC hdcMem = CreateCompatibleDC( hdc );
1211 SelectObject( hdcMem, bm );
1212 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1213 DrawFrameControl( hdcMem, &r, DFC_MENU,
1214 (lpitem->fType & MFT_RADIOCHECK) ?
1215 DFCS_MENUBULLET : DFCS_MENUCHECK );
1216 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1217 hdcMem, 0, 0, SRCCOPY );
1223 /* Draw the popup-menu arrow */
1224 if (lpitem->fType & MF_POPUP)
1226 HDC hdcMem = CreateCompatibleDC( hdc );
1227 HBITMAP hOrigBitmap;
1229 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1230 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1231 (y - arrow_bitmap_height) / 2,
1232 arrow_bitmap_width, arrow_bitmap_height,
1233 hdcMem, 0, 0, SRCCOPY );
1234 SelectObject( hdcMem, hOrigBitmap );
1238 rect.left += check_bitmap_width;
1239 rect.right -= arrow_bitmap_width;
1242 /* Done for owner-drawn */
1243 if (lpitem->fType & MF_OWNERDRAW)
1246 /* Draw the item text or bitmap */
1247 if (IS_BITMAP_ITEM(lpitem->fType))
1254 HDC hdcMem = CreateCompatibleDC( hdc );
1257 * Check if there is a magic menu item associated with this item
1258 * and load the appropriate bitmap
1260 if (IS_MAGIC_ITEM(lpitem->text))
1262 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fState & MF_HILITE),
1263 lpitem->dwItemData);
1266 resBmp = (HBITMAP)lpitem->text;
1271 GetObjectA( resBmp, sizeof(bm), &bm );
1273 SelectObject(hdcMem,resBmp );
1275 /* handle fontsize > bitmap_height */
1276 h=rect.bottom - rect.top;
1277 top = (h>bm.bmHeight) ?
1278 rect.top+(h-bm.bmHeight)/2 : rect.top;
1279 w=rect.right - rect.left;
1281 if (TWEAK_WineLook == WIN95_LOOK) {
1282 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
1283 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
1284 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1288 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
1290 BitBlt( hdc, left, top, w,
1299 /* No bitmap - process text if present */
1300 else if (IS_STRING_ITEM(lpitem->fType))
1305 UINT uFormat = (menuBar) ?
1306 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1307 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1309 if ( lpitem->fState & MFS_DEFAULT )
1311 hfontOld = SelectObject( hdc, hMenuFontBold);
1316 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1317 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1318 i = strlenW( lpitem->text );
1322 for (i = 0; lpitem->text[i]; i++)
1323 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1327 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1329 if (!(lpitem->fState & MF_HILITE) )
1331 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1332 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1333 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1334 --rect.left; --rect.top; --rect.right; --rect.bottom;
1336 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1339 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1341 /* paint the shortcut text */
1342 if (lpitem->text[i]) /* There's a tab or flush-right char */
1344 if (lpitem->text[i] == '\t')
1346 rect.left = lpitem->xTab;
1347 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1351 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1354 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1356 if (!(lpitem->fState & MF_HILITE) )
1358 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1359 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1360 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1361 --rect.left; --rect.top; --rect.right; --rect.bottom;
1363 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1365 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1369 SelectObject (hdc, hfontOld);
1374 /***********************************************************************
1375 * MENU_DrawPopupMenu
1377 * Paint a popup menu.
1379 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1381 HBRUSH hPrevBrush = 0;
1384 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1386 GetClientRect( hwnd, &rect );
1388 if(TWEAK_WineLook == WIN31_LOOK)
1390 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1391 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1394 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1395 && (SelectObject( hdc, hMenuFont)))
1399 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1401 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1407 /* draw 3-d shade */
1408 if(TWEAK_WineLook == WIN31_LOOK) {
1409 SelectObject( hdc, hShadeBrush );
1410 SetBkMode( hdc, TRANSPARENT );
1411 ropPrev = SetROP2( hdc, R2_MASKPEN );
1413 i = rect.right; /* why SetBrushOrg() doesn't? */
1414 PatBlt( hdc, i & 0xfffffffe,
1415 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1416 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1417 rect.bottom - rect.top, 0x00a000c9 );
1419 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1420 i & 0xfffffffe,rect.right - rect.left,
1421 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1422 SelectObject( hdc, hPrevPen );
1423 SelectObject( hdc, hPrevBrush );
1424 SetROP2( hdc, ropPrev );
1427 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1429 /* draw menu items */
1431 menu = MENU_GetMenu( hmenu );
1432 if (menu && menu->nItems)
1437 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1438 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1439 menu->Height, FALSE, ODA_DRAWENTIRE );
1444 SelectObject( hdc, hPrevBrush );
1449 /***********************************************************************
1452 * Paint a menu bar. Returns the height of the menu bar.
1453 * called from [windows/nonclient.c]
1455 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1462 WND *wndPtr = WIN_FindWndPtr( hwnd );
1464 lppop = MENU_GetMenu ((HMENU)wndPtr->wIDmenu );
1465 if (lppop == NULL || lprect == NULL)
1467 retvalue = GetSystemMetrics(SM_CYMENU);
1471 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1473 hfontOld = SelectObject( hDC, hMenuFont);
1475 if (lppop->Height == 0)
1476 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1478 lprect->bottom = lprect->top + lppop->Height;
1482 retvalue = lppop->Height;
1486 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1488 if (TWEAK_WineLook == WIN31_LOOK)
1490 SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1491 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1492 LineTo( hDC, lprect->right, lprect->bottom );
1496 SelectObject( hDC, GetSysColorPen(COLOR_3DFACE));
1497 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1498 LineTo( hDC, lprect->right, lprect->bottom );
1501 if (lppop->nItems == 0)
1503 retvalue = GetSystemMetrics(SM_CYMENU);
1507 for (i = 0; i < lppop->nItems; i++)
1509 MENU_DrawMenuItem( hwnd, (HMENU)wndPtr->wIDmenu, hwnd,
1510 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1512 retvalue = lppop->Height;
1516 SelectObject (hDC, hfontOld);
1518 WIN_ReleaseWndPtr(wndPtr);
1523 /***********************************************************************
1526 * Display a popup menu.
1528 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1529 INT x, INT y, INT xanchor, INT yanchor )
1532 WND *wndOwner = NULL;
1534 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1535 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1537 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1538 if (menu->FocusedItem != NO_SELECTED_ITEM)
1540 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1541 menu->FocusedItem = NO_SELECTED_ITEM;
1544 /* store the owner for DrawItem */
1545 menu->hwndOwner = hwndOwner;
1547 if( (wndOwner = WIN_FindWndPtr( hwndOwner )) )
1551 MENU_PopupMenuCalcSize( menu, hwndOwner );
1553 /* adjust popup menu pos so that it fits within the desktop */
1555 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1556 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1558 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1561 x -= width - xanchor;
1562 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1563 x = GetSystemMetrics(SM_CXSCREEN) - width;
1567 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1570 y -= height + yanchor;
1571 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1572 y = GetSystemMetrics(SM_CYSCREEN) - height;
1576 if( TWEAK_WineLook == WIN31_LOOK )
1578 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1579 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1582 /* NOTE: In Windows, top menu popup is not owned. */
1583 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1584 WS_POPUP, x, y, width, height,
1585 hwndOwner, 0, wndOwner->hInstance,
1589 WIN_ReleaseWndPtr(wndOwner);
1592 if (!top_popup) top_popup = menu->hWnd;
1594 /* Display the window */
1596 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1597 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1598 UpdateWindow( menu->hWnd );
1599 WIN_ReleaseWndPtr(wndOwner);
1606 /***********************************************************************
1609 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1610 BOOL sendMenuSelect, HMENU topmenu )
1615 TRACE("owner=0x%04x menu=0x%04x index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1617 lppop = MENU_GetMenu( hmenu );
1618 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1620 if (lppop->FocusedItem == wIndex) return;
1621 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1622 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1624 SelectObject( hdc, hMenuFont);
1626 /* Clear previous highlighted item */
1627 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1629 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1630 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1631 lppop->Height, !(lppop->wFlags & MF_POPUP),
1635 /* Highlight new item (if any) */
1636 lppop->FocusedItem = wIndex;
1637 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1639 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1640 lppop->items[wIndex].fState |= MF_HILITE;
1641 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1642 &lppop->items[wIndex], lppop->Height,
1643 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1647 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1648 SendMessageA( hwndOwner, WM_MENUSELECT,
1649 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1650 ip->fType | ip->fState | MF_MOUSESELECT |
1651 (lppop->wFlags & MF_SYSMENU)), hmenu);
1654 else if (sendMenuSelect) {
1657 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1658 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1659 MENUITEM *ip = &ptm->items[pos];
1660 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1661 ip->fType | ip->fState | MF_MOUSESELECT |
1662 (ptm->wFlags & MF_SYSMENU)), topmenu);
1666 ReleaseDC( lppop->hWnd, hdc );
1670 /***********************************************************************
1671 * MENU_MoveSelection
1673 * Moves currently selected item according to the offset parameter.
1674 * If there is no selection then it should select the last item if
1675 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1677 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1682 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1684 menu = MENU_GetMenu( hmenu );
1685 if ((!menu) || (!menu->items)) return;
1687 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1689 if( menu->nItems == 1 ) return; else
1690 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1692 if (!(menu->items[i].fType & MF_SEPARATOR))
1694 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1699 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1700 i >= 0 && i < menu->nItems ; i += offset)
1701 if (!(menu->items[i].fType & MF_SEPARATOR))
1703 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1709 /**********************************************************************
1712 * Set an item flags, id and text ptr. Called by InsertMenu() and
1715 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1718 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1720 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1721 TRACE("flags=%x str=%p\n", flags, str);
1723 if (IS_STRING_ITEM(flags))
1727 flags |= MF_SEPARATOR;
1733 /* Item beginning with a backspace is a help item */
1739 if (!(text = HEAP_strdupW( GetProcessHeap(), 0, str ))) return FALSE;
1743 else if (IS_BITMAP_ITEM(flags))
1744 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1745 else item->text = NULL;
1747 if (flags & MF_OWNERDRAW)
1748 item->dwItemData = (DWORD)str;
1750 item->dwItemData = 0;
1752 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1753 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1755 if (flags & MF_POPUP)
1757 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1758 if (menu) menu->wFlags |= MF_POPUP;
1770 if (flags & MF_POPUP)
1771 item->hSubMenu = id;
1773 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1774 flags |= MF_POPUP; /* keep popup */
1776 item->fType = flags & TYPE_MASK;
1777 item->fState = (flags & STATE_MASK) &
1778 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1781 /* Don't call SetRectEmpty here! */
1784 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1786 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1791 /**********************************************************************
1794 * Insert a new item into a menu.
1796 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1801 if (!(menu = MENU_GetMenu(hMenu)))
1804 /* Find where to insert new item */
1806 if (flags & MF_BYPOSITION) {
1807 if (pos > menu->nItems)
1810 if (!MENU_FindItem( &hMenu, &pos, flags ))
1813 if (!(menu = MENU_GetMenu( hMenu )))
1818 /* Create new items array */
1820 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1823 WARN("allocation failed\n" );
1826 if (menu->nItems > 0)
1828 /* Copy the old array into the new one */
1829 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1830 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1831 (menu->nItems-pos)*sizeof(MENUITEM) );
1832 HeapFree( GetProcessHeap(), 0, menu->items );
1834 menu->items = newItems;
1836 memset( &newItems[pos], 0, sizeof(*newItems) );
1837 menu->Height = 0; /* force size recalculate */
1838 return &newItems[pos];
1842 /**********************************************************************
1843 * MENU_ParseResource
1845 * Parse a standard menu resource and add items to the menu.
1846 * Return a pointer to the end of the resource.
1848 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1855 flags = GET_WORD(res);
1856 res += sizeof(WORD);
1857 if (!(flags & MF_POPUP))
1860 res += sizeof(WORD);
1862 if (!IS_STRING_ITEM(flags))
1863 ERR("not a string item %04x\n", flags );
1865 if (!unicode) res += strlen(str) + 1;
1866 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1867 if (flags & MF_POPUP)
1869 HMENU hSubMenu = CreatePopupMenu();
1870 if (!hSubMenu) return NULL;
1871 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1873 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1874 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1876 else /* Not a popup */
1878 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1879 else AppendMenuW( hMenu, flags, id,
1880 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1882 } while (!(flags & MF_END));
1887 /**********************************************************************
1888 * MENUEX_ParseResource
1890 * Parse an extended menu resource and add items to the menu.
1891 * Return a pointer to the end of the resource.
1893 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1899 mii.cbSize = sizeof(mii);
1900 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1901 mii.fType = GET_DWORD(res);
1902 res += sizeof(DWORD);
1903 mii.fState = GET_DWORD(res);
1904 res += sizeof(DWORD);
1905 mii.wID = GET_DWORD(res);
1906 res += sizeof(DWORD);
1907 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1908 res += sizeof(WORD);
1909 /* Align the text on a word boundary. */
1910 res += (~((int)res - 1)) & 1;
1911 mii.dwTypeData = (LPWSTR) res;
1912 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1913 /* Align the following fields on a dword boundary. */
1914 res += (~((int)res - 1)) & 3;
1916 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1917 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1919 if (resinfo & 1) { /* Pop-up? */
1920 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1921 res += sizeof(DWORD);
1922 mii.hSubMenu = CreatePopupMenu();
1925 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1926 DestroyMenu(mii.hSubMenu);
1929 mii.fMask |= MIIM_SUBMENU;
1930 mii.fType |= MF_POPUP;
1932 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1933 } while (!(resinfo & MF_END));
1938 /***********************************************************************
1941 * Return the handle of the selected sub-popup menu (if any).
1943 static HMENU MENU_GetSubPopup( HMENU hmenu )
1948 menu = MENU_GetMenu( hmenu );
1950 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1952 item = &menu->items[menu->FocusedItem];
1953 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1954 return item->hSubMenu;
1959 /***********************************************************************
1960 * MENU_HideSubPopups
1962 * Hide the sub-popup menus of this menu.
1964 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1965 BOOL sendMenuSelect )
1967 POPUPMENU *menu = MENU_GetMenu( hmenu );
1969 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1971 if (menu && top_popup)
1977 if (menu->FocusedItem != NO_SELECTED_ITEM)
1979 item = &menu->items[menu->FocusedItem];
1980 if (!(item->fType & MF_POPUP) ||
1981 !(item->fState & MF_MOUSESELECT)) return;
1982 item->fState &= ~MF_MOUSESELECT;
1983 hsubmenu = item->hSubMenu;
1986 submenu = MENU_GetMenu( hsubmenu );
1987 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1988 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1989 DestroyWindow( submenu->hWnd );
1995 /***********************************************************************
1998 * Display the sub-menu of the selected item of this menu.
1999 * Return the handle of the submenu, or hmenu if no submenu to display.
2001 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2002 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 (!(wndPtr = WIN_FindWndPtr( menu->hWnd )) ||
2015 (menu->FocusedItem == NO_SELECTED_ITEM))
2017 WIN_ReleaseWndPtr(wndPtr);
2021 item = &menu->items[menu->FocusedItem];
2022 if (!(item->fType & MF_POPUP) ||
2023 (item->fState & (MF_GRAYED | MF_DISABLED)))
2025 WIN_ReleaseWndPtr(wndPtr);
2029 /* message must be sent before using item,
2030 because nearly everything may be changed by the application ! */
2032 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2033 if (!(wFlags & TPM_NONOTIFY))
2034 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2035 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2037 item = &menu->items[menu->FocusedItem];
2040 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2041 if (!(item->fState & MF_HILITE))
2043 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2044 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2046 SelectObject( hdc, hMenuFont);
2048 item->fState |= MF_HILITE;
2049 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2050 ReleaseDC( menu->hWnd, hdc );
2052 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2055 item->fState |= MF_MOUSESELECT;
2057 if (IS_SYSTEM_MENU(menu))
2059 MENU_InitSysMenuPopup(item->hSubMenu, wndPtr->dwStyle, GetClassLongA(wndPtr->hwndSelf, GCL_STYLE));
2061 NC_GetSysPopupPos( wndPtr, &rect );
2062 rect.top = rect.bottom;
2063 rect.right = GetSystemMetrics(SM_CXSIZE);
2064 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2068 if (menu->wFlags & MF_POPUP)
2070 rect.left = wndPtr->rectWindow.left + item->rect.right - GetSystemMetrics(SM_CXBORDER);
2071 rect.top = wndPtr->rectWindow.top + item->rect.top;
2072 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2073 rect.bottom = item->rect.top - item->rect.bottom;
2077 rect.left = wndPtr->rectWindow.left + item->rect.left;
2078 rect.top = wndPtr->rectWindow.top + item->rect.bottom;
2079 rect.right = item->rect.right - item->rect.left;
2080 rect.bottom = item->rect.bottom - item->rect.top;
2084 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2085 rect.left, rect.top, rect.right, rect.bottom );
2087 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2088 WIN_ReleaseWndPtr(wndPtr);
2089 return item->hSubMenu;
2094 /**********************************************************************
2097 BOOL MENU_IsMenuActive(void)
2099 return (top_popup != 0);
2102 /***********************************************************************
2105 * Walks menu chain trying to find a menu pt maps to.
2107 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2109 POPUPMENU *menu = MENU_GetMenu( hMenu );
2110 register UINT ht = menu->FocusedItem;
2112 /* try subpopup first (if any) */
2113 ht = (ht != NO_SELECTED_ITEM &&
2114 (menu->items[ht].fType & MF_POPUP) &&
2115 (menu->items[ht].fState & MF_MOUSESELECT))
2116 ? (UINT) MENU_PtMenu(menu->items[ht].hSubMenu, pt) : 0;
2118 if( !ht ) /* check the current window (avoiding WM_HITTEST) */
2120 ht = (UINT)NC_HandleNCHitTest( menu->hWnd, pt );
2121 if( menu->wFlags & MF_POPUP )
2122 ht = (ht != (UINT)HTNOWHERE &&
2123 ht != (UINT)HTERROR) ? (UINT)hMenu : 0;
2126 WND* wndPtr = WIN_FindWndPtr(menu->hWnd);
2128 ht = ( ht == HTSYSMENU ) ? (UINT)(wndPtr->hSysMenu)
2129 : ( ht == HTMENU ) ? (UINT)(wndPtr->wIDmenu) : 0;
2130 WIN_ReleaseWndPtr(wndPtr);
2136 /***********************************************************************
2137 * MENU_ExecFocusedItem
2139 * Execute a menu item (for instance when user pressed Enter).
2140 * Return the wID of the executed item. Otherwise, -1 indicating
2141 * that no menu item was executed;
2142 * Have to receive the flags for the TrackPopupMenu options to avoid
2143 * sending unwanted message.
2146 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2149 POPUPMENU *menu = MENU_GetMenu( hMenu );
2151 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2153 if (!menu || !menu->nItems ||
2154 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2156 item = &menu->items[menu->FocusedItem];
2158 TRACE("%08x %08x %08x\n",
2159 hMenu, item->wID, item->hSubMenu);
2161 if (!(item->fType & MF_POPUP))
2163 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2165 /* If TPM_RETURNCMD is set you return the id, but
2166 do not send a message to the owner */
2167 if(!(wFlags & TPM_RETURNCMD))
2169 if( menu->wFlags & MF_SYSMENU )
2170 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2171 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2173 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2179 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2184 /***********************************************************************
2185 * MENU_SwitchTracking
2187 * Helper function for menu navigation routines.
2189 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2191 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2192 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2194 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2196 if( pmt->hTopMenu != hPtMenu &&
2197 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2199 /* both are top level menus (system and menu-bar) */
2200 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2201 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2202 pmt->hTopMenu = hPtMenu;
2204 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2205 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2209 /***********************************************************************
2212 * Return TRUE if we can go on with menu tracking.
2214 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2216 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2221 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2224 if( IS_SYSTEM_MENU(ptmenu) )
2225 item = ptmenu->items;
2227 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2231 if( ptmenu->FocusedItem != id )
2232 MENU_SwitchTracking( pmt, hPtMenu, id );
2234 /* If the popup menu is not already "popped" */
2235 if(!(item->fState & MF_MOUSESELECT ))
2237 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2239 /* In win31, a newly popped menu always remains opened for the next buttonup */
2240 if(TWEAK_WineLook == WIN31_LOOK)
2241 ptmenu->bTimeToHide = FALSE;
2246 /* Else the click was on the menu bar, finish the tracking */
2251 /***********************************************************************
2254 * Return the value of MENU_ExecFocusedItem if
2255 * the selected item was not a popup. Else open the popup.
2256 * A -1 return value indicates that we go on with menu tracking.
2259 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2261 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2266 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2269 if( IS_SYSTEM_MENU(ptmenu) )
2270 item = ptmenu->items;
2272 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2274 if( item && (ptmenu->FocusedItem == id ))
2276 if( !(item->fType & MF_POPUP) )
2277 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2279 /* If we are dealing with the top-level menu */
2280 /* and this is a click on an already "popped" item: */
2281 /* Stop the menu tracking and close the opened submenus */
2282 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2285 ptmenu->bTimeToHide = TRUE;
2291 /***********************************************************************
2294 * Return TRUE if we can go on with menu tracking.
2296 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2298 UINT id = NO_SELECTED_ITEM;
2299 POPUPMENU *ptmenu = NULL;
2303 ptmenu = MENU_GetMenu( hPtMenu );
2304 if( IS_SYSTEM_MENU(ptmenu) )
2307 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2310 if( id == NO_SELECTED_ITEM )
2312 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2313 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2316 else if( ptmenu->FocusedItem != id )
2318 MENU_SwitchTracking( pmt, hPtMenu, id );
2319 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2325 /***********************************************************************
2328 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2330 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2332 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2334 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2335 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2341 LRESULT l = SendMessageA( pmt->hOwnerWnd, WM_NEXTMENU, vk,
2342 (IS_SYSTEM_MENU(menu)) ? GetSubMenu16(pmt->hTopMenu,0) : pmt->hTopMenu );
2344 TRACE("%04x [%04x] -> %04x [%04x]\n",
2345 (UINT16)pmt->hCurrentMenu, (UINT16)pmt->hOwnerWnd, LOWORD(l), HIWORD(l) );
2349 wndPtr = WIN_FindWndPtr(pmt->hOwnerWnd);
2351 hNewWnd = pmt->hOwnerWnd;
2352 if( IS_SYSTEM_MENU(menu) )
2354 /* switch to the menu bar */
2356 if( wndPtr->dwStyle & WS_CHILD || !wndPtr->wIDmenu )
2358 WIN_ReleaseWndPtr(wndPtr);
2362 hNewMenu = wndPtr->wIDmenu;
2365 menu = MENU_GetMenu( hNewMenu );
2366 id = menu->nItems - 1;
2369 else if( wndPtr->dwStyle & WS_SYSMENU )
2371 /* switch to the system menu */
2372 hNewMenu = wndPtr->hSysMenu;
2376 WIN_ReleaseWndPtr(wndPtr);
2379 WIN_ReleaseWndPtr(wndPtr);
2381 else /* application returned a new menu to switch to */
2383 hNewMenu = LOWORD(l); hNewWnd = HIWORD(l);
2385 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2387 wndPtr = WIN_FindWndPtr(hNewWnd);
2389 if( wndPtr->dwStyle & WS_SYSMENU &&
2390 GetSubMenu16(wndPtr->hSysMenu, 0) == hNewMenu )
2392 /* get the real system menu */
2393 hNewMenu = wndPtr->hSysMenu;
2395 else if( wndPtr->dwStyle & WS_CHILD || wndPtr->wIDmenu != hNewMenu )
2397 /* FIXME: Not sure what to do here;
2398 * perhaps try to track hNewMenu as a popup? */
2400 TRACE(" -- got confused.\n");
2401 WIN_ReleaseWndPtr(wndPtr);
2404 WIN_ReleaseWndPtr(wndPtr);
2409 if( hNewMenu != pmt->hTopMenu )
2411 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2413 if( pmt->hCurrentMenu != pmt->hTopMenu )
2414 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2417 if( hNewWnd != pmt->hOwnerWnd )
2420 pmt->hOwnerWnd = hNewWnd;
2421 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2424 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2425 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2432 /***********************************************************************
2435 * The idea is not to show the popup if the next input message is
2436 * going to hide it anyway.
2438 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2442 msg.hwnd = pmt->hOwnerWnd;
2444 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2445 pmt->trackFlags |= TF_SKIPREMOVE;
2450 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2451 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2453 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2454 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2455 if( msg.message == WM_KEYDOWN &&
2456 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2458 pmt->trackFlags |= TF_SUSPENDPOPUP;
2465 /* failures go through this */
2466 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2470 /***********************************************************************
2473 * Handle a VK_ESCAPE key event in a menu.
2475 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2477 BOOL bEndMenu = TRUE;
2479 if (pmt->hCurrentMenu != pmt->hTopMenu)
2481 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2483 if (menu->wFlags & MF_POPUP)
2485 HMENU hmenutmp, hmenuprev;
2487 hmenuprev = hmenutmp = pmt->hTopMenu;
2489 /* close topmost popup */
2490 while (hmenutmp != pmt->hCurrentMenu)
2492 hmenuprev = hmenutmp;
2493 hmenutmp = MENU_GetSubPopup( hmenuprev );
2496 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2497 pmt->hCurrentMenu = hmenuprev;
2505 /***********************************************************************
2508 * Handle a VK_LEFT key event in a menu.
2510 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2513 HMENU hmenutmp, hmenuprev;
2516 hmenuprev = hmenutmp = pmt->hTopMenu;
2517 menu = MENU_GetMenu( hmenutmp );
2519 /* Try to move 1 column left (if possible) */
2520 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2521 NO_SELECTED_ITEM ) {
2523 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2528 /* close topmost popup */
2529 while (hmenutmp != pmt->hCurrentMenu)
2531 hmenuprev = hmenutmp;
2532 hmenutmp = MENU_GetSubPopup( hmenuprev );
2535 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2536 pmt->hCurrentMenu = hmenuprev;
2538 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2540 /* move menu bar selection if no more popups are left */
2542 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2543 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2545 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2547 /* A sublevel menu was displayed - display the next one
2548 * unless there is another displacement coming up */
2550 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2551 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2552 pmt->hTopMenu, TRUE, wFlags);
2558 /***********************************************************************
2561 * Handle a VK_RIGHT key event in a menu.
2563 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2566 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2569 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2571 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2573 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2575 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2577 /* If already displaying a popup, try to display sub-popup */
2579 hmenutmp = pmt->hCurrentMenu;
2580 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2582 /* if subpopup was displayed then we are done */
2583 if (hmenutmp != pmt->hCurrentMenu) return;
2586 /* Check to see if there's another column */
2587 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2588 NO_SELECTED_ITEM ) {
2589 TRACE("Going to %d.\n", nextcol );
2590 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2595 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2597 if( pmt->hCurrentMenu != pmt->hTopMenu )
2599 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2600 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2601 } else hmenutmp = 0;
2603 /* try to move to the next item */
2604 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2605 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2607 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2608 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2609 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2610 pmt->hTopMenu, TRUE, wFlags);
2614 /***********************************************************************
2617 * Menu tracking code.
2619 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2620 HWND hwnd, const RECT *lprect )
2625 INT executedMenuId = -1;
2627 BOOL enterIdleSent = FALSE;
2630 mt.hCurrentMenu = hmenu;
2631 mt.hTopMenu = hmenu;
2632 mt.hOwnerWnd = hwnd;
2636 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2637 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2638 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2641 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2643 if (wFlags & TPM_BUTTONDOWN)
2645 /* Get the result in order to start the tracking or not */
2646 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2647 fEndMenu = !fRemove;
2650 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2654 menu = MENU_GetMenu( mt.hCurrentMenu );
2655 if (!menu) /* sometimes happens if I do a window manager close */
2657 msg.hwnd = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2659 /* we have to keep the message in the queue until it's
2660 * clear that menu loop is not over yet. */
2662 if (!MSG_InternalGetMessage( &msg, msg.hwnd, mt.hOwnerWnd, 0, 0,
2663 MSGF_MENU, PM_NOREMOVE, !enterIdleSent, &enterIdleSent )) break;
2665 /* check if EndMenu() tried to cancel us, by posting this message */
2666 if(msg.message == WM_CANCELMODE)
2668 /* we are now out of the loop */
2671 /* remove the message from the queue */
2672 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2674 /* break out of internal loop, ala ESCAPE */
2678 TranslateMessage( &msg );
2681 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2682 enterIdleSent=FALSE;
2685 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2688 * use the mouse coordinates in lParam instead of those in the MSG
2689 * struct to properly handle synthetic messages. lParam coords are
2690 * relative to client area, so they must be converted; since they can
2691 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2693 mt.pt.x = SLOWORD(msg.lParam);
2694 mt.pt.y = SHIWORD(msg.lParam);
2695 ClientToScreen(msg.hwnd,&mt.pt);
2697 /* Find a menu for this mouse event */
2698 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2702 /* no WM_NC... messages in captured state */
2704 case WM_RBUTTONDBLCLK:
2705 case WM_RBUTTONDOWN:
2706 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2708 case WM_LBUTTONDBLCLK:
2709 case WM_LBUTTONDOWN:
2710 /* If the message belongs to the menu, removes it from the queue */
2711 /* Else, end menu tracking */
2712 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2713 fEndMenu = !fRemove;
2717 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2720 /* Check if a menu was selected by the mouse */
2723 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2725 /* End the loop if executedMenuId is an item ID */
2726 /* or if the job was done (executedMenuId = 0). */
2727 fEndMenu = fRemove = (executedMenuId != -1);
2729 /* No menu was selected by the mouse */
2730 /* if the function was called by TrackPopupMenu, continue
2731 with the menu tracking. If not, stop it */
2733 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2738 /* In win95 winelook, the selected menu item must be changed every time the
2739 mouse moves. In Win31 winelook, the mouse button has to be held down */
2741 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2742 ( (msg.wParam & MK_LBUTTON) ||
2743 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2745 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2747 } /* switch(msg.message) - mouse */
2749 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2751 fRemove = TRUE; /* Keyboard messages are always removed */
2759 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2760 NO_SELECTED_ITEM, FALSE, 0 );
2763 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2764 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2767 case VK_DOWN: /* If on menu bar, pull-down the menu */
2769 menu = MENU_GetMenu( mt.hCurrentMenu );
2770 if (!(menu->wFlags & MF_POPUP))
2771 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2772 else /* otherwise try to move selection */
2773 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2777 MENU_KeyLeft( &mt, wFlags );
2781 MENU_KeyRight( &mt, wFlags );
2785 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2791 hi.cbSize = sizeof(HELPINFO);
2792 hi.iContextType = HELPINFO_MENUITEM;
2793 if (menu->FocusedItem == NO_SELECTED_ITEM)
2796 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2797 hi.hItemHandle = hmenu;
2798 hi.dwContextId = menu->dwContextHelpID;
2799 hi.MousePos = msg.pt;
2800 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2807 break; /* WM_KEYDOWN */
2817 break; /* WM_SYSKEYDOWN */
2823 if (msg.wParam == '\r' || msg.wParam == ' ')
2825 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2826 fEndMenu = (executedMenuId != -1);
2831 /* Hack to avoid control chars. */
2832 /* We will find a better way real soon... */
2833 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2835 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2836 LOWORD(msg.wParam), FALSE );
2837 if (pos == (UINT)-2) fEndMenu = TRUE;
2838 else if (pos == (UINT)-1) MessageBeep(0);
2841 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2843 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2844 fEndMenu = (executedMenuId != -1);
2848 } /* switch(msg.message) - kbd */
2852 DispatchMessageA( &msg );
2855 if (!fEndMenu) fRemove = TRUE;
2857 /* finally remove message from the queue */
2859 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2860 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2861 else mt.trackFlags &= ~TF_SKIPREMOVE;
2866 /* If dropdown is still painted and the close box is clicked on
2867 then the menu will be destroyed as part of the DispatchMessage above.
2868 This will then invalidate the menu handle in mt.hTopMenu. We should
2869 check for this first. */
2870 if( IsMenu( mt.hTopMenu ) )
2872 menu = MENU_GetMenu( mt.hTopMenu );
2874 if( IsWindow( mt.hOwnerWnd ) )
2876 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2878 if (menu && menu->wFlags & MF_POPUP)
2880 DestroyWindow( menu->hWnd );
2883 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2884 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2887 /* Reset the variable for hiding menu */
2888 if( menu ) menu->bTimeToHide = FALSE;
2891 /* The return value is only used by TrackPopupMenu */
2892 return ((executedMenuId != -1) ? executedMenuId : 0);
2895 /***********************************************************************
2898 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2900 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
2904 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2905 if (!(wFlags & TPM_NONOTIFY))
2906 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2908 SendMessageA( hWnd, WM_SETCURSOR, hWnd, HTCAPTION );
2910 if (!(wFlags & TPM_NONOTIFY))
2913 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
2914 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2915 { /* app changed/recreated menu bar entries in WM_INITMENU
2916 Recalculate menu sizes else clicks will not work */
2918 HDC hdc = GetDCEx( hWnd, 0, DCX_CACHE | DCX_WINDOW );
2919 SelectObject( hdc, hMenuFont);
2920 GetClientRect(hWnd, &r); /* probably too simple */
2921 MENU_MenuBarCalcSize( hdc, &r, menu, hWnd );
2922 ReleaseDC(hWnd, hdc);
2927 /***********************************************************************
2930 static BOOL MENU_ExitTracking(HWND hWnd)
2932 TRACE("hwnd=0x%04x\n", hWnd);
2934 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
2939 /***********************************************************************
2940 * MENU_TrackMouseMenuBar
2942 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2944 void MENU_TrackMouseMenuBar( WND* wndPtr, INT ht, POINT pt )
2946 HWND hWnd = wndPtr->hwndSelf;
2947 HMENU hMenu = (ht == HTSYSMENU) ? wndPtr->hSysMenu : wndPtr->wIDmenu;
2948 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2950 TRACE("pwnd=%p ht=0x%04x (%ld,%ld)\n", wndPtr, ht, pt.x, pt.y);
2954 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2955 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2956 MENU_ExitTracking(hWnd);
2961 /***********************************************************************
2962 * MENU_TrackKbdMenuBar
2964 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2966 void MENU_TrackKbdMenuBar( WND* wndPtr, UINT wParam, INT vkey)
2968 UINT uItem = NO_SELECTED_ITEM;
2970 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2972 /* find window that has a menu */
2974 while( wndPtr->dwStyle & WS_CHILD)
2975 if( !(wndPtr = wndPtr->parent) ) return;
2977 /* check if we have to track a system menu */
2979 if( (wndPtr->dwStyle & (WS_CHILD | WS_MINIMIZE)) ||
2980 !wndPtr->wIDmenu || vkey == VK_SPACE )
2982 if( !(wndPtr->dwStyle & WS_SYSMENU) ) return;
2983 hTrackMenu = wndPtr->hSysMenu;
2985 wParam |= HTSYSMENU; /* prevent item lookup */
2988 hTrackMenu = wndPtr->wIDmenu;
2990 if (IsMenu( hTrackMenu ))
2992 MENU_InitTracking( wndPtr->hwndSelf, hTrackMenu, FALSE, wFlags );
2994 if( vkey && vkey != VK_SPACE )
2996 uItem = MENU_FindItemByKey( wndPtr->hwndSelf, hTrackMenu,
2997 vkey, (wParam & HTSYSMENU) );
2998 if( uItem >= (UINT)(-2) )
3000 if( uItem == (UINT)(-1) ) MessageBeep(0);
3007 MENU_SelectItem( wndPtr->hwndSelf, hTrackMenu, uItem, TRUE, 0 );
3009 if( uItem == NO_SELECTED_ITEM )
3010 MENU_MoveSelection( wndPtr->hwndSelf, hTrackMenu, ITEM_NEXT );
3012 PostMessageA( wndPtr->hwndSelf, WM_KEYDOWN, VK_DOWN, 0L );
3014 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, wndPtr->hwndSelf, NULL );
3017 MENU_ExitTracking (wndPtr->hwndSelf);
3022 /**********************************************************************
3023 * TrackPopupMenu (USER.416)
3025 BOOL16 WINAPI TrackPopupMenu16( HMENU16 hMenu, UINT16 wFlags, INT16 x, INT16 y,
3026 INT16 nReserved, HWND16 hWnd, const RECT16 *lpRect )
3030 CONV_RECT16TO32( lpRect, &r );
3031 return TrackPopupMenu( hMenu, wFlags, x, y, nReserved, hWnd,
3032 lpRect ? &r : NULL );
3036 /**********************************************************************
3037 * TrackPopupMenu (USER32.@)
3039 * Like the win32 API, the function return the command ID only if the
3040 * flag TPM_RETURNCMD is on.
3043 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3044 INT nReserved, HWND hWnd, const RECT *lpRect )
3048 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3050 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3051 if (!(wFlags & TPM_NONOTIFY))
3052 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3054 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3055 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3056 MENU_ExitTracking(hWnd);
3058 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3064 /**********************************************************************
3065 * TrackPopupMenuEx (USER32.@)
3067 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3068 HWND hWnd, LPTPMPARAMS lpTpm )
3070 FIXME("not fully implemented\n" );
3071 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3072 lpTpm ? &lpTpm->rcExclude : NULL );
3075 /***********************************************************************
3078 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3080 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3082 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3083 hwnd, message, wParam, lParam);
3089 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3090 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3094 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3095 return MA_NOACTIVATE;
3100 BeginPaint( hwnd, &ps );
3101 MENU_DrawPopupMenu( hwnd, ps.hdc,
3102 (HMENU)GetWindowLongA( hwnd, 0 ) );
3103 EndPaint( hwnd, &ps );
3110 /* zero out global pointer in case resident popup window was destroyed. */
3111 if (hwnd == top_popup) top_popup = 0;
3118 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3121 SetWindowLongW( hwnd, 0, 0 );
3124 case MM_SETMENUHANDLE:
3125 SetWindowLongW( hwnd, 0, wParam );
3128 case MM_GETMENUHANDLE:
3129 return GetWindowLongW( hwnd, 0 );
3132 return DefWindowProcW( hwnd, message, wParam, lParam );
3138 /***********************************************************************
3139 * MENU_GetMenuBarHeight
3141 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3143 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3144 INT orgX, INT orgY )
3152 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3153 hwnd, menubarWidth, orgX, orgY );
3155 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
3158 if (!(lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu)))
3160 WIN_ReleaseWndPtr(wndPtr);
3164 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3165 SelectObject( hdc, hMenuFont);
3166 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3167 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3168 ReleaseDC( hwnd, hdc );
3169 retvalue = lppop->Height;
3170 WIN_ReleaseWndPtr(wndPtr);
3175 /*******************************************************************
3176 * ChangeMenu (USER.153)
3178 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3179 UINT16 id, UINT16 flags )
3181 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3182 hMenu, pos, (DWORD)data, id, flags );
3183 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3186 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3187 /* for MF_DELETE. We should check the parameters for all others */
3188 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3190 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3191 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3193 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3194 flags & MF_BYPOSITION ? pos : id,
3195 flags & ~MF_REMOVE );
3196 /* Default: MF_INSERT */
3197 return InsertMenu16( hMenu, pos, flags, id, data );
3201 /*******************************************************************
3202 * ChangeMenuA (USER32.@)
3204 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3205 UINT id, UINT flags )
3207 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3208 hMenu, pos, (DWORD)data, id, flags );
3209 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3211 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3212 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3214 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3215 flags & MF_BYPOSITION ? pos : id,
3216 flags & ~MF_REMOVE );
3217 /* Default: MF_INSERT */
3218 return InsertMenuA( hMenu, pos, flags, id, data );
3222 /*******************************************************************
3223 * ChangeMenuW (USER32.@)
3225 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3226 UINT id, UINT flags )
3228 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3229 hMenu, pos, (DWORD)data, id, flags );
3230 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3232 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3233 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3235 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3236 flags & MF_BYPOSITION ? pos : id,
3237 flags & ~MF_REMOVE );
3238 /* Default: MF_INSERT */
3239 return InsertMenuW( hMenu, pos, flags, id, data );
3243 /*******************************************************************
3244 * CheckMenuItem (USER.154)
3246 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3248 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3252 /*******************************************************************
3253 * CheckMenuItem (USER32.@)
3255 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3260 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3261 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3262 ret = item->fState & MF_CHECKED;
3263 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3264 else item->fState &= ~MF_CHECKED;
3269 /**********************************************************************
3270 * EnableMenuItem (USER.155)
3272 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3274 return EnableMenuItem( hMenu, wItemID, wFlags );
3278 /**********************************************************************
3279 * EnableMenuItem (USER32.@)
3281 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3287 TRACE("(%04x, %04X, %04X) !\n",
3288 hMenu, wItemID, wFlags);
3290 /* Get the Popupmenu to access the owner menu */
3291 if (!(menu = MENU_GetMenu(hMenu)))
3294 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3297 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3298 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3300 /* In win95 if the close item in the system menu change update the close button */
3301 if (TWEAK_WineLook == WIN95_LOOK)
3302 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3304 if (menu->hSysMenuOwner != 0)
3306 POPUPMENU* parentMenu;
3308 /* Get the parent menu to access*/
3309 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3312 /* Refresh the frame to reflect the change*/
3313 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3314 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3322 /*******************************************************************
3323 * GetMenuString (USER.161)
3325 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3326 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3328 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3332 /*******************************************************************
3333 * GetMenuStringA (USER32.@)
3335 INT WINAPI GetMenuStringA(
3336 HMENU hMenu, /* [in] menuhandle */
3337 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3338 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3339 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3340 UINT wFlags /* [in] MF_ flags */
3344 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3345 hMenu, wItemID, str, nMaxSiz, wFlags );
3346 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3347 if (!IS_STRING_ITEM(item->fType)) return 0;
3348 if (!str || !nMaxSiz) return strlenW(item->text);
3350 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3352 TRACE("returning '%s'\n", str );
3357 /*******************************************************************
3358 * GetMenuStringW (USER32.@)
3360 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3361 LPWSTR str, INT nMaxSiz, UINT wFlags )
3365 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3366 hMenu, wItemID, str, nMaxSiz, wFlags );
3367 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3368 if (!IS_STRING_ITEM(item->fType)) return 0;
3369 if (!str || !nMaxSiz) return strlenW(item->text);
3371 lstrcpynW( str, item->text, nMaxSiz );
3372 return strlenW(str);
3376 /**********************************************************************
3377 * HiliteMenuItem (USER.162)
3379 BOOL16 WINAPI HiliteMenuItem16( HWND16 hWnd, HMENU16 hMenu, UINT16 wItemID,
3382 return HiliteMenuItem( hWnd, hMenu, wItemID, wHilite );
3386 /**********************************************************************
3387 * HiliteMenuItem (USER32.@)
3389 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3393 TRACE("(%04x, %04x, %04x, %04x);\n",
3394 hWnd, hMenu, wItemID, wHilite);
3395 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3396 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3397 if (menu->FocusedItem == wItemID) return TRUE;
3398 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3399 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3404 /**********************************************************************
3405 * GetMenuState (USER.250)
3407 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3409 return GetMenuState( hMenu, wItemID, wFlags );
3413 /**********************************************************************
3414 * GetMenuState (USER32.@)
3416 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3419 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3420 hMenu, wItemID, wFlags);
3421 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3422 debug_print_menuitem (" item: ", item, "");
3423 if (item->fType & MF_POPUP)
3425 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3426 if (!menu) return -1;
3427 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3431 /* We used to (from way back then) mask the result to 0xff. */
3432 /* I don't know why and it seems wrong as the documented */
3433 /* return flag MF_SEPARATOR is outside that mask. */
3434 return (item->fType | item->fState);
3439 /**********************************************************************
3440 * GetMenuItemCount (USER.263)
3442 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3444 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3445 if (!menu) return -1;
3446 TRACE("(%04x) returning %d\n",
3447 hMenu, menu->nItems );
3448 return menu->nItems;
3452 /**********************************************************************
3453 * GetMenuItemCount (USER32.@)
3455 INT WINAPI GetMenuItemCount( HMENU hMenu )
3457 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3458 if (!menu) return -1;
3459 TRACE("(%04x) returning %d\n",
3460 hMenu, menu->nItems );
3461 return menu->nItems;
3464 /**********************************************************************
3465 * GetMenuItemID (USER.264)
3467 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3469 return (UINT16) GetMenuItemID (hMenu, nPos);
3472 /**********************************************************************
3473 * GetMenuItemID (USER32.@)
3475 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3479 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3480 if (lpmi->fType & MF_POPUP) return -1;
3485 /*******************************************************************
3486 * InsertMenu (USER.410)
3488 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3489 UINT16 id, SEGPTR data )
3491 UINT pos32 = (UINT)pos;
3492 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3493 if (IS_STRING_ITEM(flags) && data)
3494 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3495 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3499 /*******************************************************************
3500 * InsertMenuW (USER32.@)
3502 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3503 UINT id, LPCWSTR str )
3507 if (IS_STRING_ITEM(flags) && str)
3508 TRACE("hMenu %04x, pos %d, flags %08x, "
3509 "id %04x, str %s\n",
3510 hMenu, pos, flags, id, debugstr_w(str) );
3511 else TRACE("hMenu %04x, pos %d, flags %08x, "
3512 "id %04x, str %08lx (not a string)\n",
3513 hMenu, pos, flags, id, (DWORD)str );
3515 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3517 if (!(MENU_SetItemData( item, flags, id, str )))
3519 RemoveMenu( hMenu, pos, flags );
3523 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3524 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3526 item->hCheckBit = item->hUnCheckBit = 0;
3531 /*******************************************************************
3532 * InsertMenuA (USER32.@)
3534 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3535 UINT id, LPCSTR str )
3539 if (IS_STRING_ITEM(flags) && str)
3541 LPWSTR newstr = HEAP_strdupAtoW( GetProcessHeap(), 0, str );
3542 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3543 HeapFree( GetProcessHeap(), 0, newstr );
3546 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3550 /*******************************************************************
3551 * AppendMenu (USER.411)
3553 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3555 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3559 /*******************************************************************
3560 * AppendMenuA (USER32.@)
3562 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3563 UINT id, LPCSTR data )
3565 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3569 /*******************************************************************
3570 * AppendMenuW (USER32.@)
3572 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3573 UINT id, LPCWSTR data )
3575 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3579 /**********************************************************************
3580 * RemoveMenu (USER.412)
3582 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3584 return RemoveMenu( hMenu, nPos, wFlags );
3588 /**********************************************************************
3589 * RemoveMenu (USER32.@)
3591 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3596 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3597 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3598 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3602 MENU_FreeItemData( item );
3604 if (--menu->nItems == 0)
3606 HeapFree( GetProcessHeap(), 0, menu->items );
3611 while(nPos < menu->nItems)
3617 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3618 menu->nItems * sizeof(MENUITEM) );
3624 /**********************************************************************
3625 * DeleteMenu (USER.413)
3627 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3629 return DeleteMenu( hMenu, nPos, wFlags );
3633 /**********************************************************************
3634 * DeleteMenu (USER32.@)
3636 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3638 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3639 if (!item) return FALSE;
3640 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3641 /* nPos is now the position of the item */
3642 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3647 /*******************************************************************
3648 * ModifyMenu (USER.414)
3650 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3651 UINT16 id, SEGPTR data )
3653 if (IS_STRING_ITEM(flags))
3654 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3655 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3659 /*******************************************************************
3660 * ModifyMenuW (USER32.@)
3662 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3663 UINT id, LPCWSTR str )
3667 if (IS_STRING_ITEM(flags))
3669 TRACE("%04x %d %04x %04x %s\n",
3670 hMenu, pos, flags, id, debugstr_w(str) );
3671 if (!str) return FALSE;
3675 TRACE("%04x %d %04x %04x %08lx\n",
3676 hMenu, pos, flags, id, (DWORD)str );
3679 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3680 return MENU_SetItemData( item, flags, id, str );
3684 /*******************************************************************
3685 * ModifyMenuA (USER32.@)
3687 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3688 UINT id, LPCSTR str )
3692 if (IS_STRING_ITEM(flags) && str)
3694 LPWSTR newstr = HEAP_strdupAtoW( GetProcessHeap(), 0, str );
3695 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3696 HeapFree( GetProcessHeap(), 0, newstr );
3699 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3703 /**********************************************************************
3704 * CreatePopupMenu (USER.415)
3706 HMENU16 WINAPI CreatePopupMenu16(void)
3708 return CreatePopupMenu();
3712 /**********************************************************************
3713 * CreatePopupMenu (USER32.@)
3715 HMENU WINAPI CreatePopupMenu(void)
3720 if (!(hmenu = CreateMenu())) return 0;
3721 menu = MENU_GetMenu( hmenu );
3722 menu->wFlags |= MF_POPUP;
3723 menu->bTimeToHide = FALSE;
3728 /**********************************************************************
3729 * GetMenuCheckMarkDimensions (USER.417) (USER32.@)
3731 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3733 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3737 /**********************************************************************
3738 * SetMenuItemBitmaps (USER.418)
3740 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3741 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3743 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3747 /**********************************************************************
3748 * SetMenuItemBitmaps (USER32.@)
3750 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3751 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3754 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3755 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3756 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3758 if (!hNewCheck && !hNewUnCheck)
3760 item->fState &= ~MF_USECHECKBITMAPS;
3762 else /* Install new bitmaps */
3764 item->hCheckBit = hNewCheck;
3765 item->hUnCheckBit = hNewUnCheck;
3766 item->fState |= MF_USECHECKBITMAPS;
3772 /**********************************************************************
3773 * CreateMenu (USER.151)
3775 HMENU16 WINAPI CreateMenu16(void)
3777 return CreateMenu();
3781 /**********************************************************************
3782 * CreateMenu (USER32.@)
3784 HMENU WINAPI CreateMenu(void)
3788 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3789 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3791 ZeroMemory(menu, sizeof(POPUPMENU));
3792 menu->wMagic = MENU_MAGIC;
3793 menu->FocusedItem = NO_SELECTED_ITEM;
3794 menu->bTimeToHide = FALSE;
3796 TRACE("return %04x\n", hMenu );
3802 /**********************************************************************
3803 * DestroyMenu (USER.152)
3805 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3807 return DestroyMenu( hMenu );
3811 /**********************************************************************
3812 * DestroyMenu (USER32.@)
3814 BOOL WINAPI DestroyMenu( HMENU hMenu )
3816 TRACE("(%04x)\n", hMenu);
3818 /* Silently ignore attempts to destroy default system popup */
3820 if (hMenu && hMenu != MENU_DefSysPopup)
3822 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3824 if (!lppop) return FALSE;
3826 lppop->wMagic = 0; /* Mark it as destroyed */
3828 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3830 DestroyWindow( lppop->hWnd );
3834 if (lppop->items) /* recursively destroy submenus */
3837 MENUITEM *item = lppop->items;
3838 for (i = lppop->nItems; i > 0; i--, item++)
3840 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3841 MENU_FreeItemData( item );
3843 HeapFree( GetProcessHeap(), 0, lppop->items );
3845 USER_HEAP_FREE( hMenu );
3847 return (hMenu != MENU_DefSysPopup);
3851 /**********************************************************************
3852 * GetSystemMenu (USER.156)
3854 HMENU16 WINAPI GetSystemMenu16( HWND16 hWnd, BOOL16 bRevert )
3856 return GetSystemMenu( hWnd, bRevert );
3860 /**********************************************************************
3861 * GetSystemMenu (USER32.@)
3863 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3865 WND *wndPtr = WIN_FindWndPtr( hWnd );
3870 if( wndPtr->hSysMenu )
3874 DestroyMenu(wndPtr->hSysMenu);
3875 wndPtr->hSysMenu = 0;
3879 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3882 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3883 menu->items[0].hSubMenu = MENU_CopySysPopup();
3887 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
3888 wndPtr->hSysMenu, hWnd);
3889 wndPtr->hSysMenu = 0;
3894 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3895 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3897 if( wndPtr->hSysMenu )
3900 retvalue = GetSubMenu16(wndPtr->hSysMenu, 0);
3902 /* Store the dummy sysmenu handle to facilitate the refresh */
3903 /* of the close button if the SC_CLOSE item change */
3904 menu = MENU_GetMenu(retvalue);
3906 menu->hSysMenuOwner = wndPtr->hSysMenu;
3908 WIN_ReleaseWndPtr(wndPtr);
3910 return bRevert ? 0 : retvalue;
3914 /*******************************************************************
3915 * SetSystemMenu (USER.280)
3917 BOOL16 WINAPI SetSystemMenu16( HWND16 hwnd, HMENU16 hMenu )
3919 return SetSystemMenu( hwnd, hMenu );
3923 /*******************************************************************
3924 * SetSystemMenu (USER32.@)
3926 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3928 WND *wndPtr = WIN_FindWndPtr(hwnd);
3932 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3933 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3934 WIN_ReleaseWndPtr(wndPtr);
3941 /**********************************************************************
3942 * GetMenu (USER.157)
3944 HMENU16 WINAPI GetMenu16( HWND16 hWnd )
3946 return (HMENU16)GetMenu(hWnd);
3950 /**********************************************************************
3951 * GetMenu (USER32.@)
3953 HMENU WINAPI GetMenu( HWND hWnd )
3956 WND * wndPtr = WIN_FindWndPtr(hWnd);
3958 if (!wndPtr) return 0;
3960 retvalue = (HMENU)wndPtr->wIDmenu;
3961 TRACE("for %swindow %04x returning %04x\n",
3962 (wndPtr->dwStyle & WS_CHILD) ? "child " : "", hWnd, retvalue);
3963 WIN_ReleaseWndPtr(wndPtr);
3968 /**********************************************************************
3969 * SetMenu (USER.158)
3971 BOOL16 WINAPI SetMenu16( HWND16 hWnd, HMENU16 hMenu )
3973 return SetMenu( hWnd, hMenu );
3977 /**********************************************************************
3978 * SetMenu (USER32.@)
3980 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3982 WND * wndPtr = WIN_FindWndPtr(hWnd);
3985 TRACE("(%04x, %04x);\n", hWnd, hMenu);
3987 if (hMenu && !IsMenu(hMenu))
3989 WARN("hMenu is not a menu handle\n");
3993 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD))
3995 if (GetCapture() == hWnd) ReleaseCapture();
3997 wndPtr->wIDmenu = (UINT)hMenu;
4002 if (!(lpmenu = MENU_GetMenu(hMenu)))
4005 lpmenu->hWnd = hWnd;
4006 lpmenu->Height = 0; /* Make sure we recalculate the size */
4008 if (IsWindowVisible(hWnd))
4009 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4010 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4014 WIN_ReleaseWndPtr(wndPtr);
4020 /**********************************************************************
4021 * GetSubMenu (USER.159)
4023 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
4025 return GetSubMenu( hMenu, nPos );
4029 /**********************************************************************
4030 * GetSubMenu (USER32.@)
4032 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4036 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
4037 if (!(lpmi->fType & MF_POPUP)) return 0;
4038 return lpmi->hSubMenu;
4042 /**********************************************************************
4043 * DrawMenuBar (USER.160)
4045 void WINAPI DrawMenuBar16( HWND16 hWnd )
4047 DrawMenuBar( hWnd );
4051 /**********************************************************************
4052 * DrawMenuBar (USER32.@)
4054 BOOL WINAPI DrawMenuBar( HWND hWnd )
4057 WND *wndPtr = WIN_FindWndPtr(hWnd);
4058 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD) && wndPtr->wIDmenu)
4060 lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu);
4063 WIN_ReleaseWndPtr(wndPtr);
4067 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4068 lppop->hwndOwner = hWnd;
4069 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4070 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4071 WIN_ReleaseWndPtr(wndPtr);
4074 WIN_ReleaseWndPtr(wndPtr);
4078 /***********************************************************************
4079 * DrawMenuBarTemp (USER32.@)
4081 DWORD WINAPI DrawMenuBarTemp(DWORD p1, DWORD p2)
4083 FIXME("(%08lx %08lx): stub\n", p1, p2);
4087 /***********************************************************************
4088 * EndMenu (USER.187) (USER32.@)
4090 void WINAPI EndMenu(void)
4092 /* if we are in the menu code, and it is active */
4093 if (!fEndMenu && top_popup)
4095 /* terminate the menu handling code */
4098 /* needs to be posted to wakeup the internal menu handler */
4099 /* which will now terminate the menu, in the event that */
4100 /* the main window was minimized, or lost focus, so we */
4101 /* don't end up with an orphaned menu */
4102 PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
4107 /***********************************************************************
4108 * LookupMenuHandle (USER.217)
4110 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4112 HMENU hmenu32 = hmenu;
4114 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4115 else return hmenu32;
4119 /**********************************************************************
4120 * LoadMenu (USER.150)
4122 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4128 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4132 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4135 if (!name) return 0;
4137 /* check for Win32 module */
4138 if (HIWORD(instance)) return LoadMenuA( instance, name );
4139 instance = GetExePtr( instance );
4141 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4142 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4143 hMenu = LoadMenuIndirect16(LockResource16(handle));
4144 FreeResource16( handle );
4149 /*****************************************************************
4150 * LoadMenuA (USER32.@)
4152 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4154 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4155 if (!hrsrc) return 0;
4156 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4160 /*****************************************************************
4161 * LoadMenuW (USER32.@)
4163 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4165 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4166 if (!hrsrc) return 0;
4167 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4171 /**********************************************************************
4172 * LoadMenuIndirect (USER.220)
4174 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4177 WORD version, offset;
4178 LPCSTR p = (LPCSTR)template;
4180 TRACE("(%p)\n", template );
4181 version = GET_WORD(p);
4185 WARN("version must be 0 for Win16\n" );
4188 offset = GET_WORD(p);
4189 p += sizeof(WORD) + offset;
4190 if (!(hMenu = CreateMenu())) return 0;
4191 if (!MENU_ParseResource( p, hMenu, FALSE ))
4193 DestroyMenu( hMenu );
4200 /**********************************************************************
4201 * LoadMenuIndirectA (USER32.@)
4203 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4206 WORD version, offset;
4207 LPCSTR p = (LPCSTR)template;
4209 TRACE("%p\n", template );
4210 version = GET_WORD(p);
4215 offset = GET_WORD(p);
4216 p += sizeof(WORD) + offset;
4217 if (!(hMenu = CreateMenu())) return 0;
4218 if (!MENU_ParseResource( p, hMenu, TRUE ))
4220 DestroyMenu( hMenu );
4225 offset = GET_WORD(p);
4226 p += sizeof(WORD) + offset;
4227 if (!(hMenu = CreateMenu())) return 0;
4228 if (!MENUEX_ParseResource( p, hMenu))
4230 DestroyMenu( hMenu );
4235 ERR("version %d not supported.\n", version);
4241 /**********************************************************************
4242 * LoadMenuIndirectW (USER32.@)
4244 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4246 /* FIXME: is there anything different between A and W? */
4247 return LoadMenuIndirectA( template );
4251 /**********************************************************************
4254 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4256 return IsMenu( hmenu );
4260 /**********************************************************************
4263 BOOL WINAPI IsMenu(HMENU hmenu)
4265 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4266 return menu != NULL;
4269 /**********************************************************************
4270 * GetMenuItemInfo_common
4273 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4274 LPMENUITEMINFOW lpmii, BOOL unicode)
4276 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4278 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4283 if (lpmii->fMask & MIIM_TYPE) {
4284 lpmii->fType = menu->fType;
4285 switch (MENU_ITEM_TYPE(menu->fType)) {
4287 break; /* will be done below */
4290 lpmii->dwTypeData = menu->text;
4297 /* copy the text string */
4298 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4299 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4304 len = strlenW(menu->text);
4305 if(lpmii->dwTypeData && lpmii->cch)
4306 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4310 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4311 if(lpmii->dwTypeData && lpmii->cch)
4312 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4313 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4314 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4316 /* if we've copied a substring we return its length */
4317 if(lpmii->dwTypeData && lpmii->cch)
4319 if (lpmii->cch <= len) lpmii->cch--;
4321 else /* return length of string */
4325 if (lpmii->fMask & MIIM_FTYPE)
4326 lpmii->fType = menu->fType;
4328 if (lpmii->fMask & MIIM_BITMAP)
4329 lpmii->hbmpItem = menu->hbmpItem;
4331 if (lpmii->fMask & MIIM_STATE)
4332 lpmii->fState = menu->fState;
4334 if (lpmii->fMask & MIIM_ID)
4335 lpmii->wID = menu->wID;
4337 if (lpmii->fMask & MIIM_SUBMENU)
4338 lpmii->hSubMenu = menu->hSubMenu;
4340 if (lpmii->fMask & MIIM_CHECKMARKS) {
4341 lpmii->hbmpChecked = menu->hCheckBit;
4342 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4344 if (lpmii->fMask & MIIM_DATA)
4345 lpmii->dwItemData = menu->dwItemData;
4350 /**********************************************************************
4351 * GetMenuItemInfoA (USER32.@)
4353 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4354 LPMENUITEMINFOA lpmii)
4356 return GetMenuItemInfo_common (hmenu, item, bypos,
4357 (LPMENUITEMINFOW)lpmii, FALSE);
4360 /**********************************************************************
4361 * GetMenuItemInfoW (USER32.@)
4363 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4364 LPMENUITEMINFOW lpmii)
4366 return GetMenuItemInfo_common (hmenu, item, bypos,
4370 /**********************************************************************
4371 * SetMenuItemInfo_common
4374 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4375 const MENUITEMINFOW *lpmii,
4378 if (!menu) return FALSE;
4380 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4382 if (lpmii->fMask & MIIM_TYPE ) {
4383 /* Get rid of old string. */
4384 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4385 HeapFree(GetProcessHeap(), 0, menu->text);
4389 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4390 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4391 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4393 menu->text = lpmii->dwTypeData;
4395 if (IS_STRING_ITEM(menu->fType)) {
4398 menu->text = HEAP_strdupW(GetProcessHeap(), 0, lpmii->dwTypeData);
4400 menu->text = HEAP_strdupAtoW(GetProcessHeap(), 0, (LPSTR)lpmii->dwTypeData);
4403 menu->fType |= MF_SEPARATOR;
4407 if (lpmii->fMask & MIIM_FTYPE ) {
4408 /* free the string when the type is changing */
4409 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4410 HeapFree(GetProcessHeap(), 0, menu->text);
4413 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4414 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4415 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4416 menu->fType |= MF_SEPARATOR;
4419 if (lpmii->fMask & MIIM_STRING ) {
4420 /* free the string when used */
4421 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4422 HeapFree(GetProcessHeap(), 0, menu->text);
4423 if (lpmii->dwTypeData) {
4425 menu->text = HEAP_strdupW(GetProcessHeap(), 0, lpmii->dwTypeData);
4427 menu->text = HEAP_strdupAtoW(GetProcessHeap(), 0, (LPSTR) lpmii->dwTypeData);
4430 menu->fType |= MF_SEPARATOR;
4434 if (lpmii->fMask & MIIM_STATE)
4436 /* fixme: MFS_DEFAULT do we have to reset the other menu items? */
4437 menu->fState = lpmii->fState;
4440 if (lpmii->fMask & MIIM_ID)
4441 menu->wID = lpmii->wID;
4443 if (lpmii->fMask & MIIM_SUBMENU) {
4444 menu->hSubMenu = lpmii->hSubMenu;
4445 if (menu->hSubMenu) {
4446 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4448 subMenu->wFlags |= MF_POPUP;
4449 menu->fType |= MF_POPUP;
4452 /* FIXME: Return an error ? */
4453 menu->fType &= ~MF_POPUP;
4456 menu->fType &= ~MF_POPUP;
4459 if (lpmii->fMask & MIIM_CHECKMARKS)
4461 if (lpmii->fType & MFT_RADIOCHECK)
4462 menu->fType |= MFT_RADIOCHECK;
4464 menu->hCheckBit = lpmii->hbmpChecked;
4465 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4467 if (lpmii->fMask & MIIM_DATA)
4468 menu->dwItemData = lpmii->dwItemData;
4470 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4474 /**********************************************************************
4475 * SetMenuItemInfoA (USER32.@)
4477 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4478 const MENUITEMINFOA *lpmii)
4480 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4481 (const MENUITEMINFOW *)lpmii, FALSE);
4484 /**********************************************************************
4485 * SetMenuItemInfoW (USER32.@)
4487 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4488 const MENUITEMINFOW *lpmii)
4490 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4494 /**********************************************************************
4495 * SetMenuDefaultItem (USER32.@)
4498 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4504 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4506 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4508 /* reset all default-item flags */
4510 for (i = 0; i < menu->nItems; i++, item++)
4512 item->fState &= ~MFS_DEFAULT;
4515 /* no default item */
4524 if ( uItem >= menu->nItems ) return FALSE;
4525 item[uItem].fState |= MFS_DEFAULT;
4530 for (i = 0; i < menu->nItems; i++, item++)
4532 if (item->wID == uItem)
4534 item->fState |= MFS_DEFAULT;
4543 /**********************************************************************
4544 * GetMenuDefaultItem (USER32.@)
4546 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4552 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4554 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4556 /* find default item */
4560 if (! item) return -1;
4562 while ( !( item->fState & MFS_DEFAULT ) )
4565 if (i >= menu->nItems ) return -1;
4568 /* default: don't return disabled items */
4569 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4571 /* search rekursiv when needed */
4572 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4575 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4576 if ( -1 != ret ) return ret;
4578 /* when item not found in submenu, return the popup item */
4580 return ( bypos ) ? i : item->wID;
4584 /*******************************************************************
4585 * InsertMenuItem (USER.441)
4589 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4590 const MENUITEMINFO16 *mii )
4594 miia.cbSize = sizeof(miia);
4595 miia.fMask = mii->fMask;
4596 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4597 miia.fType = mii->fType;
4598 miia.fState = mii->fState;
4599 miia.wID = mii->wID;
4600 miia.hSubMenu = mii->hSubMenu;
4601 miia.hbmpChecked = mii->hbmpChecked;
4602 miia.hbmpUnchecked = mii->hbmpUnchecked;
4603 miia.dwItemData = mii->dwItemData;
4604 miia.cch = mii->cch;
4605 if (IS_STRING_ITEM(miia.fType))
4606 miia.dwTypeData = MapSL(mii->dwTypeData);
4607 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4611 /**********************************************************************
4612 * InsertMenuItemA (USER32.@)
4614 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4615 const MENUITEMINFOA *lpmii)
4617 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4618 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4622 /**********************************************************************
4623 * InsertMenuItemW (USER32.@)
4625 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4626 const MENUITEMINFOW *lpmii)
4628 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4629 return SetMenuItemInfo_common(item, lpmii, TRUE);
4632 /**********************************************************************
4633 * CheckMenuRadioItem (USER32.@)
4636 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4637 UINT first, UINT last, UINT check,
4640 MENUITEM *mifirst, *milast, *micheck;
4641 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4643 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4644 hMenu, first, last, check, bypos);
4646 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4647 milast = MENU_FindItem (&mlast, &last, bypos);
4648 micheck = MENU_FindItem (&mcheck, &check, bypos);
4650 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4651 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4652 micheck > milast || micheck < mifirst)
4655 while (mifirst <= milast)
4657 if (mifirst == micheck)
4659 mifirst->fType |= MFT_RADIOCHECK;
4660 mifirst->fState |= MFS_CHECKED;
4662 mifirst->fType &= ~MFT_RADIOCHECK;
4663 mifirst->fState &= ~MFS_CHECKED;
4671 /**********************************************************************
4672 * CheckMenuRadioItem (not a Windows API)
4675 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4676 UINT16 first, UINT16 last, UINT16 check,
4679 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4682 /**********************************************************************
4683 * GetMenuItemRect (USER32.@)
4685 * ATTENTION: Here, the returned values in rect are the screen
4686 * coordinates of the item just like if the menu was
4687 * always on the upper left side of the application.
4690 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4693 POPUPMENU *itemMenu;
4697 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4699 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4700 referenceHwnd = hwnd;
4704 itemMenu = MENU_GetMenu(hMenu);
4705 if (itemMenu == NULL)
4708 if(itemMenu->hWnd == 0)
4710 referenceHwnd = itemMenu->hWnd;
4713 if ((rect == NULL) || (item == NULL))
4718 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4723 /**********************************************************************
4724 * GetMenuItemRect (USER.665)
4727 BOOL16 WINAPI GetMenuItemRect16 (HWND16 hwnd, HMENU16 hMenu, UINT16 uItem,
4733 if (!rect) return FALSE;
4734 res = GetMenuItemRect (hwnd, hMenu, uItem, &r32);
4735 CONV_RECT32TO16 (&r32, rect);
4739 /**********************************************************************
4740 * SetMenuInfo (USER32.@)
4743 * MIM_APPLYTOSUBMENUS
4744 * actually use the items to draw the menu
4746 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4750 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4752 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4755 if (lpmi->fMask & MIM_BACKGROUND)
4756 menu->hbrBack = lpmi->hbrBack;
4758 if (lpmi->fMask & MIM_HELPID)
4759 menu->dwContextHelpID = lpmi->dwContextHelpID;
4761 if (lpmi->fMask & MIM_MAXHEIGHT)
4762 menu->cyMax = lpmi->cyMax;
4764 if (lpmi->fMask & MIM_MENUDATA)
4765 menu->dwMenuData = lpmi->dwMenuData;
4767 if (lpmi->fMask & MIM_STYLE)
4768 menu->dwStyle = lpmi->dwStyle;
4775 /**********************************************************************
4776 * GetMenuInfo (USER32.@)
4782 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4785 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4787 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4790 if (lpmi->fMask & MIM_BACKGROUND)
4791 lpmi->hbrBack = menu->hbrBack;
4793 if (lpmi->fMask & MIM_HELPID)
4794 lpmi->dwContextHelpID = menu->dwContextHelpID;
4796 if (lpmi->fMask & MIM_MAXHEIGHT)
4797 lpmi->cyMax = menu->cyMax;
4799 if (lpmi->fMask & MIM_MENUDATA)
4800 lpmi->dwMenuData = menu->dwMenuData;
4802 if (lpmi->fMask & MIM_STYLE)
4803 lpmi->dwStyle = menu->dwStyle;
4810 /**********************************************************************
4811 * SetMenuContextHelpId (USER.384)
4813 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4815 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4819 /**********************************************************************
4820 * SetMenuContextHelpId (USER32.@)
4822 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4826 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4828 if ((menu = MENU_GetMenu(hMenu)))
4830 menu->dwContextHelpID = dwContextHelpID;
4836 /**********************************************************************
4837 * GetMenuContextHelpId (USER.385)
4839 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4841 return GetMenuContextHelpId( hMenu );
4844 /**********************************************************************
4845 * GetMenuContextHelpId (USER32.@)
4847 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4851 TRACE("(0x%04x)\n", hMenu);
4853 if ((menu = MENU_GetMenu(hMenu)))
4855 return menu->dwContextHelpID;
4860 /**********************************************************************
4861 * MenuItemFromPoint (USER32.@)
4863 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4865 FIXME("(0x%04x,0x%04x,(%ld,%ld)):stub\n",
4866 hWnd, hMenu, ptScreen.x, ptScreen.y);
4871 /**********************************************************************
4872 * translate_accelerator
4874 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4875 BYTE fVirt, WORD key, WORD cmd )
4879 if (wParam != key) return FALSE;
4881 if (message == WM_CHAR)
4883 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4885 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4891 if(fVirt & FVIRTKEY)
4894 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4895 wParam, 0xff & HIWORD(lParam));
4896 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4897 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4898 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4899 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4900 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4904 if (!(lParam & 0x01000000)) /* no special_key */
4906 if ((fVirt & FALT) && (lParam & 0x20000000))
4907 { /* ^^ ALT pressed */
4908 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4917 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4919 else if (GetCapture())
4921 else if (!IsWindowEnabled(hWnd))
4925 HMENU hMenu, hSubMenu, hSysMenu;
4926 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4927 WND* wndPtr = WIN_FindWndPtr(hWnd);
4929 hMenu = (wndPtr->dwStyle & WS_CHILD) ? 0 : (HMENU)wndPtr->wIDmenu;
4930 hSysMenu = wndPtr->hSysMenu;
4931 WIN_ReleaseWndPtr(wndPtr);
4933 /* find menu item and ask application to initialize it */
4934 /* 1. in the system menu */
4935 hSubMenu = hSysMenu;
4937 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4939 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4940 if(hSubMenu != hSysMenu)
4942 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4943 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4944 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4946 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4948 else /* 2. in the window's menu */
4952 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4954 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4955 if(hSubMenu != hMenu)
4957 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4958 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
4959 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4961 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4965 if (uSysStat != (UINT)-1)
4967 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4974 if (uStat != (UINT)-1)
4980 if (uStat & (MF_DISABLED|MF_GRAYED))
4991 if( mesg==WM_COMMAND )
4993 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4994 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
4996 else if( mesg==WM_SYSCOMMAND )
4998 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4999 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
5003 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5004 * #0: unknown (please report!)
5005 * #1: for WM_KEYUP,WM_SYSKEYUP
5006 * #2: mouse is captured
5007 * #3: window is disabled
5008 * #4: it's a disabled system menu option
5009 * #5: it's a menu option, but window is iconic
5010 * #6: it's a menu option, but disabled
5012 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5014 ERR_(accel)(" unknown reason - please report!");
5019 /**********************************************************************
5020 * TranslateAccelerator (USER32.@)
5021 * TranslateAcceleratorW (USER32.@)
5023 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
5026 LPACCEL16 lpAccelTbl;
5031 WARN_(accel)("msg null; should hang here to be win compatible\n");
5034 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5036 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5039 if ((msg->message != WM_KEYDOWN &&
5040 msg->message != WM_KEYUP &&
5041 msg->message != WM_SYSKEYDOWN &&
5042 msg->message != WM_SYSKEYUP &&
5043 msg->message != WM_CHAR)) return 0;
5045 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5046 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
5047 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5052 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5053 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5055 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5056 WARN_(accel)("couldn't translate accelerator key\n");
5061 /**********************************************************************
5062 * TranslateAccelerator (USER.178)
5064 INT16 WINAPI TranslateAccelerator16( HWND16 hWnd, HACCEL16 hAccel, LPMSG16 msg )
5066 LPACCEL16 lpAccelTbl;
5071 WARN_(accel)("msg null; should hang here to be win compatible\n");
5074 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5076 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5079 if ((msg->message != WM_KEYDOWN &&
5080 msg->message != WM_KEYUP &&
5081 msg->message != WM_SYSKEYDOWN &&
5082 msg->message != WM_SYSKEYUP &&
5083 msg->message != WM_CHAR)) return 0;
5085 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5086 "msg->hwnd=%04x, msg->message=%04x, wParam=%04x, lParam=%lx\n",
5087 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5092 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5093 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd ))
5095 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5096 WARN_(accel)("couldn't translate accelerator key\n");