4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Note: the style MF_MOUSESELECT is used to mark popup items that
26 * have been selected, i.e. their popup menu is currently displayed.
27 * This is probably not the meaning this style has in MS-Windows.
39 #include "wine/port.h"
48 #include "wine/winbase16.h"
49 #include "wine/winuser16.h"
51 #include "wine/server.h"
52 #include "wine/unicode.h"
55 #include "user_private.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(menu);
59 WINE_DECLARE_DEBUG_CHANNEL(accel);
61 /* internal popup menu window messages */
63 #define MM_SETMENUHANDLE (WM_USER + 0)
64 #define MM_GETMENUHANDLE (WM_USER + 1)
66 /* Menu item structure */
68 /* ----------- MENUITEMINFO Stuff ----------- */
69 UINT fType; /* Item type. */
70 UINT fState; /* Item state. */
71 UINT_PTR wID; /* Item id. */
72 HMENU hSubMenu; /* Pop-up menu. */
73 HBITMAP hCheckBit; /* Bitmap when checked. */
74 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
75 LPWSTR text; /* Item text. */
76 ULONG_PTR dwItemData; /* Application defined. */
77 LPWSTR dwTypeData; /* depends on fMask */
78 HBITMAP hbmpItem; /* bitmap */
79 /* ----------- Wine stuff ----------- */
80 RECT rect; /* Item area (relative to menu window) */
81 UINT xTab; /* X position of text after Tab */
84 /* Popup menu structure */
86 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
87 WORD wMagic; /* Magic number */
88 WORD Width; /* Width of the whole menu */
89 WORD Height; /* Height of the whole menu */
90 UINT nItems; /* Number of items in the menu */
91 HWND hWnd; /* Window containing the menu */
92 MENUITEM *items; /* Array of menu items */
93 UINT FocusedItem; /* Currently focused item */
94 HWND hwndOwner; /* window receiving the messages for ownerdraw */
95 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
96 /* ------------ MENUINFO members ------ */
97 DWORD dwStyle; /* Extended mennu style */
98 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
99 HBRUSH hbrBack; /* brush for menu background */
100 DWORD dwContextHelpID;
101 DWORD dwMenuData; /* application defined value */
102 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
103 SIZE maxBmpSize; /* Maximum size of the bitmap items in MIIM_BITMAP state */
104 } POPUPMENU, *LPPOPUPMENU;
106 /* internal flags for menu tracking */
108 #define TF_ENDMENU 0x0001
109 #define TF_SUSPENDPOPUP 0x0002
110 #define TF_SKIPREMOVE 0x0004
115 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
116 HMENU hTopMenu; /* initial menu */
117 HWND hOwnerWnd; /* where notifications are sent */
121 #define MENU_MAGIC 0x554d /* 'MU' */
126 /* Internal MENU_TrackMenu() flags */
127 #define TPM_INTERNAL 0xF0000000
128 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
129 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
130 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
132 /* Space between 2 menu bar items */
133 #define MENU_BAR_ITEMS_SPACE 12
135 /* Minimum width of a tab character */
136 #define MENU_TAB_SPACE 8
138 /* Height of a separator item */
139 #define SEPARATOR_HEIGHT 5
141 /* Space between 2 columns */
142 #define MENU_COL_SPACE 4
144 /* (other menu->FocusedItem values give the position of the focused item) */
145 #define NO_SELECTED_ITEM 0xffff
147 #define MENU_ITEM_TYPE(flags) \
148 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
150 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
151 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
153 #define IS_SYSTEM_MENU(menu) \
154 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
156 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
157 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
158 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
159 MF_POPUP | MF_SYSMENU | MF_HELP)
160 #define STATE_MASK (~TYPE_MASK)
162 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
164 static SIZE menucharsize;
165 static UINT ODitemheight; /* default owner drawn item height */
167 /* Use global popup window because there's no way 2 menus can
168 * be tracked at the same time. */
169 static HWND top_popup;
171 /* Flag set by EndMenu() to force an exit from menu tracking */
172 static BOOL fEndMenu = FALSE;
174 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
176 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
178 /*********************************************************************
179 * menu class descriptor
181 const struct builtin_class_descr MENU_builtin_class =
183 POPUPMENU_CLASS_ATOMA, /* name */
184 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
185 NULL, /* procA (winproc is Unicode only) */
186 PopupMenuWndProc, /* procW */
187 sizeof(HMENU), /* extra */
188 IDC_ARROW, /* cursor */
189 (HBRUSH)(COLOR_MENU+1) /* brush */
193 /***********************************************************************
194 * debug_print_menuitem
196 * Print a menuitem in readable form.
199 #define debug_print_menuitem(pre, mp, post) \
200 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
202 #define MENUOUT(text) \
203 TRACE("%s%s", (count++ ? "," : ""), (text))
205 #define MENUFLAG(bit,text) \
207 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
210 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
213 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
214 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "HBMMENU_MBAR_CLOSE",
215 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
216 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
217 TRACE("%s ", prefix);
219 UINT flags = mp->fType;
220 TRACE( "{ ID=0x%x", mp->wID);
222 TRACE( ", Sub=%p", mp->hSubMenu);
226 MENUFLAG( MFT_SEPARATOR, "sep");
227 MENUFLAG( MFT_OWNERDRAW, "own");
228 MENUFLAG( MFT_BITMAP, "bit");
229 MENUFLAG(MF_POPUP, "pop");
230 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
231 MENUFLAG(MFT_MENUBREAK, "brk");
232 MENUFLAG(MFT_RADIOCHECK, "radio");
233 MENUFLAG(MFT_RIGHTORDER, "rorder");
234 MENUFLAG(MF_SYSMENU, "sys");
235 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
237 TRACE( "+0x%x", flags);
243 MENUFLAG(MFS_GRAYED, "grey");
244 MENUFLAG(MFS_DEFAULT, "default");
245 MENUFLAG(MFS_DISABLED, "dis");
246 MENUFLAG(MFS_CHECKED, "check");
247 MENUFLAG(MFS_HILITE, "hi");
248 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
249 MENUFLAG(MF_MOUSESELECT, "mouse");
251 TRACE( "+0x%x", flags);
254 TRACE( ", Chk=%p", mp->hCheckBit);
256 TRACE( ", Unc=%p", mp->hUnCheckBit);
258 TRACE( ", Text=%s", debugstr_w(mp->text));
260 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
263 if( IS_MAGIC_BITMAP(mp->hbmpItem))
264 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
266 TRACE( ", hbitmap=%p", mp->hbmpItem);
271 TRACE(" %s\n", postfix);
278 /***********************************************************************
281 * Validate the given menu handle and returns the menu structure pointer.
283 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
285 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
286 if (!menu || menu->wMagic != MENU_MAGIC)
288 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
294 /***********************************************************************
297 * Get the system menu of a window
299 static HMENU get_win_sys_menu( HWND hwnd )
302 WND *win = WIN_GetPtr( hwnd );
303 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
306 WIN_ReleasePtr( win );
311 /***********************************************************************
314 static HFONT get_menu_font( BOOL bold )
316 static HFONT hMenuFont, hMenuFontBold;
318 HFONT ret = bold ? hMenuFontBold : hMenuFont;
322 NONCLIENTMETRICSW ncm;
325 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
326 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
330 ncm.lfMenuFont.lfWeight += 300;
331 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
333 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
334 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
338 /* another thread beat us to it */
346 /***********************************************************************
349 static HBITMAP get_arrow_bitmap(void)
351 static HBITMAP arrow_bitmap;
353 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
357 /***********************************************************************
360 * Return the default system menu.
362 static HMENU MENU_CopySysPopup(void)
364 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
365 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
368 POPUPMENU* menu = MENU_GetMenu(hMenu);
369 menu->wFlags |= MF_SYSMENU | MF_POPUP;
370 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
373 ERR("Unable to load default system menu\n" );
375 TRACE("returning %p.\n", hMenu );
381 /**********************************************************************
384 * Create a copy of the system menu. System menu in Windows is
385 * a special menu bar with the single entry - system menu popup.
386 * This popup is presented to the outside world as a "system menu".
387 * However, the real system menu handle is sometimes seen in the
388 * WM_MENUSELECT parameters (and Word 6 likes it this way).
390 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
394 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
395 if ((hMenu = CreateMenu()))
397 POPUPMENU *menu = MENU_GetMenu(hMenu);
398 menu->wFlags = MF_SYSMENU;
399 menu->hWnd = WIN_GetFullHandle( hWnd );
400 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
403 hPopupMenu = MENU_CopySysPopup();
407 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
408 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
410 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
411 (UINT_PTR)hPopupMenu, NULL );
413 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
414 menu->items[0].fState = 0;
415 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
417 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
420 DestroyMenu( hMenu );
422 ERR("failed to load system menu!\n");
427 /***********************************************************************
428 * MENU_InitSysMenuPopup
430 * Grey the appropriate items in System menu.
432 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
436 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
437 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
438 gray = ((style & WS_MAXIMIZE) != 0);
439 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
440 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
441 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
442 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
443 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
444 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
445 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
446 gray = (clsStyle & CS_NOCLOSE) != 0;
448 /* The menu item must keep its state if it's disabled */
450 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
454 /******************************************************************************
456 * UINT MENU_GetStartOfNextColumn(
459 *****************************************************************************/
461 static UINT MENU_GetStartOfNextColumn(
464 POPUPMENU *menu = MENU_GetMenu(hMenu);
468 return NO_SELECTED_ITEM;
470 i = menu->FocusedItem + 1;
471 if( i == NO_SELECTED_ITEM )
474 for( ; i < menu->nItems; ++i ) {
475 if (menu->items[i].fType & MF_MENUBARBREAK)
479 return NO_SELECTED_ITEM;
483 /******************************************************************************
485 * UINT MENU_GetStartOfPrevColumn(
488 *****************************************************************************/
490 static UINT MENU_GetStartOfPrevColumn(
493 POPUPMENU *menu = MENU_GetMenu(hMenu);
497 return NO_SELECTED_ITEM;
499 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
500 return NO_SELECTED_ITEM;
502 /* Find the start of the column */
504 for(i = menu->FocusedItem; i != 0 &&
505 !(menu->items[i].fType & MF_MENUBARBREAK);
509 return NO_SELECTED_ITEM;
511 for(--i; i != 0; --i) {
512 if (menu->items[i].fType & MF_MENUBARBREAK)
516 TRACE("ret %d.\n", i );
523 /***********************************************************************
526 * Find a menu item. Return a pointer on the item, and modifies *hmenu
527 * in case the item was in a sub-menu.
529 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
532 MENUITEM *fallback = NULL;
535 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
536 if (wFlags & MF_BYPOSITION)
538 if (*nPos >= menu->nItems) return NULL;
539 return &menu->items[*nPos];
543 MENUITEM *item = menu->items;
544 for (i = 0; i < menu->nItems; i++, item++)
546 if (item->fType & MF_POPUP)
548 HMENU hsubmenu = item->hSubMenu;
549 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
555 if ((UINT_PTR)item->hSubMenu == *nPos)
556 fallback = item; /* fallback to this item if nothing else found */
558 else if (item->wID == *nPos)
568 /***********************************************************************
571 * Find a Sub menu. Return the position of the submenu, and modifies
572 * *hmenu in case it is found in another sub-menu.
573 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
575 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
580 if (((*hmenu)==(HMENU)0xffff) ||
581 (!(menu = MENU_GetMenu(*hmenu))))
582 return NO_SELECTED_ITEM;
584 for (i = 0; i < menu->nItems; i++, item++) {
585 if(!(item->fType & MF_POPUP)) continue;
586 if (item->hSubMenu == hSubTarget) {
590 HMENU hsubmenu = item->hSubMenu;
591 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
592 if (pos != NO_SELECTED_ITEM) {
598 return NO_SELECTED_ITEM;
601 /***********************************************************************
604 static void MENU_FreeItemData( MENUITEM* item )
607 HeapFree( GetProcessHeap(), 0, item->text );
610 /***********************************************************************
611 * MENU_FindItemByCoords
613 * Find the item at the specified coordinates (screen coords). Does
614 * not work for child windows and therefore should not be called for
615 * an arbitrary system menu.
617 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
618 POINT pt, UINT *pos )
624 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
625 pt.x -= wrect.left;pt.y -= wrect.top;
627 for (i = 0; i < menu->nItems; i++, item++)
629 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
630 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
640 /***********************************************************************
643 * Find the menu item selected by a key press.
644 * Return item id, -1 if none, -2 if we should close the menu.
646 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
647 WCHAR key, BOOL forceMenuChar )
649 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
651 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
655 POPUPMENU *menu = MENU_GetMenu( hmenu );
656 MENUITEM *item = menu->items;
663 for (i = 0; i < menu->nItems; i++, item++)
667 WCHAR *p = item->text - 2;
670 p = strchrW (p + 2, '&');
672 while (p != NULL && p [1] == '&');
673 if (p && (toupperW(p[1]) == toupperW(key))) return i;
677 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
678 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
679 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
680 if (HIWORD(menuchar) == 1) return (UINT)(-2);
686 /***********************************************************************
687 * MENU_GetBitmapItemSize
689 * Get the size of a bitmap item.
691 static void MENU_GetBitmapItemSize( HBITMAP bmp, DWORD data, SIZE *size )
695 size->cx = size->cy = 0;
697 /* check if there is a magic menu item associated with this item */
698 switch( (INT_PTR)bmp )
700 case (INT_PTR)HBMMENU_SYSTEM:
707 case (INT_PTR)HBMMENU_MBAR_RESTORE:
708 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
709 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
710 case (INT_PTR)HBMMENU_MBAR_CLOSE:
711 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
712 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
715 case (INT_PTR)HBMMENU_CALLBACK:
716 case (INT_PTR)HBMMENU_POPUP_CLOSE:
717 case (INT_PTR)HBMMENU_POPUP_RESTORE:
718 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
719 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
720 FIXME("Magic %p not implemented\n", bmp );
723 if (GetObjectW(bmp, sizeof(bm), &bm ))
725 size->cx = bm.bmWidth;
726 size->cy = bm.bmHeight;
730 /***********************************************************************
731 * MENU_DrawBitmapItem
733 * Draw a bitmap item.
735 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar)
741 int w = rect->right - rect->left;
742 int h = rect->bottom - rect->top;
745 HBITMAP hbmToDraw = lpitem->hbmpItem;
748 /* Check if there is a magic menu item associated with this item */
749 if (IS_MAGIC_BITMAP(hbmToDraw))
754 switch((INT_PTR)hbmToDraw)
756 case (INT_PTR)HBMMENU_SYSTEM:
757 if (lpitem->dwItemData)
759 bmp = (HBITMAP)lpitem->dwItemData;
760 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
764 static HBITMAP hBmpSysMenu;
766 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
768 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
769 /* only use right half of the bitmap */
770 bmp_xoffset = bm.bmWidth / 2;
771 bm.bmWidth -= bmp_xoffset;
774 case (INT_PTR)HBMMENU_MBAR_RESTORE:
775 flags = DFCS_CAPTIONRESTORE;
777 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
778 flags = DFCS_CAPTIONMIN;
780 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
781 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
783 case (INT_PTR)HBMMENU_MBAR_CLOSE:
784 flags = DFCS_CAPTIONCLOSE;
786 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
787 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
789 case (INT_PTR)HBMMENU_CALLBACK:
790 case (INT_PTR)HBMMENU_POPUP_CLOSE:
791 case (INT_PTR)HBMMENU_POPUP_RESTORE:
792 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
793 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
795 FIXME("Magic %p not implemented\n", hbmToDraw);
799 InflateRect( &r, -1, -1 );
800 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
801 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
805 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
808 hdcMem = CreateCompatibleDC( hdc );
809 SelectObject( hdcMem, bmp );
811 /* handle fontsize > bitmap_height */
812 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
814 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
815 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
816 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
817 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
822 /***********************************************************************
825 * Calculate the size of the menu item and store it in lpitem->rect.
827 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
828 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
831 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
832 UINT arrow_bitmap_width;
835 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
836 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
837 (menuBar ? " (MenuBar)" : ""));
839 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
840 arrow_bitmap_width = bm.bmWidth;
842 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
844 if (lpitem->fType & MF_OWNERDRAW)
846 MEASUREITEMSTRUCT mis;
847 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
848 if( !menucharsize.cx ) {
849 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
850 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
851 * but it is unlikely an application will depend on that */
852 ODitemheight = HIWORD( GetDialogBaseUnits());
854 mis.CtlType = ODT_MENU;
856 mis.itemID = lpitem->wID;
857 mis.itemData = lpitem->dwItemData;
858 mis.itemHeight = ODitemheight;
860 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
861 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
862 * width of a menufont character to the width of an owner-drawn menu.
864 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
866 /* under at least win95 you seem to be given a standard
867 height for the menu and the height value is ignored */
868 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
870 lpitem->rect.bottom += mis.itemHeight;
872 TRACE("id=%04x size=%ldx%ld\n",
873 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
874 lpitem->rect.bottom-lpitem->rect.top);
878 if (lpitem->fType & MF_SEPARATOR)
880 lpitem->rect.bottom += SEPARATOR_HEIGHT;
886 if (lpitem->hbmpItem)
888 if (lpitem->hbmpItem == HBMMENU_CALLBACK)
890 MEASUREITEMSTRUCT measItem;
891 measItem.CtlType = ODT_MENU;
893 measItem.itemID = lpitem->wID;
894 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
895 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
896 measItem.itemData = lpitem->dwItemData;
898 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
900 /* Keep the size of the bitmap in callback mode to be able to draw it correctly */
901 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, measItem.itemWidth - (lpitem->rect.right - lpitem->rect.left));
902 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, measItem.itemHeight - (lpitem->rect.bottom - lpitem->rect.top));
903 lpitem->rect.right = lpitem->rect.left + measItem.itemWidth;
906 MENU_GetBitmapItemSize(lpitem->hbmpItem, lpitem->dwItemData, &size);
907 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, size.cx);
908 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, size.cy);
909 lpitem->rect.right += size.cx;
910 lpitem->rect.bottom += size.cy;
912 if (lppop->dwStyle & MNS_CHECKORBMP)
913 lpitem->rect.right += check_bitmap_width;
915 lpitem->rect.right += 2 * check_bitmap_width;
917 lpitem->rect.right += 2 * check_bitmap_width;
918 if (lpitem->fType & MF_POPUP)
919 lpitem->rect.right += arrow_bitmap_width;
920 } else if (lpitem->hbmpItem)
924 MENU_GetBitmapItemSize( (HBITMAP) lpitem->hbmpItem, lpitem->dwItemData, &size );
925 lpitem->rect.right += size.cx;
926 lpitem->rect.bottom += size.cy;
927 /* Leave space for the sunken border */
928 lpitem->rect.right += 2;
929 lpitem->rect.bottom += 2;
932 /* it must be a text item - unless it's the system menu */
933 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text)
936 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
938 lpitem->rect.right += size.cx;
939 lpitem->rect.bottom += max(max(size.cy, GetSystemMetrics(SM_CYMENU)-1), lppop->maxBmpSize.cy);
944 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
946 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
948 /* Item contains a tab (only meaningful in popup menus) */
949 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
950 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
951 lpitem->rect.right += MENU_TAB_SPACE;
955 if (strchrW( lpitem->text, '\b' ))
956 lpitem->rect.right += MENU_TAB_SPACE;
957 lpitem->xTab = lpitem->rect.right - check_bitmap_width
958 - arrow_bitmap_width;
961 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
965 /***********************************************************************
966 * MENU_PopupMenuCalcSize
968 * Calculate the size of a popup menu.
970 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
975 int orgX, orgY, maxX, maxTab, maxTabWidth;
977 lppop->Width = lppop->Height = 0;
978 if (lppop->nItems == 0) return;
981 SelectObject( hdc, get_menu_font(FALSE));
986 lppop->maxBmpSize.cx = 0;
987 lppop->maxBmpSize.cy = 0;
989 while (start < lppop->nItems)
991 lpitem = &lppop->items[start];
993 if( lpitem->fType & MF_MENUBREAK)
994 orgX += MENU_COL_SPACE;
997 maxTab = maxTabWidth = 0;
998 /* Parse items until column break or end of menu */
999 for (i = start; i < lppop->nItems; i++, lpitem++)
1002 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1004 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1006 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1007 maxX = max( maxX, lpitem->rect.right );
1008 orgY = lpitem->rect.bottom;
1009 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1011 maxTab = max( maxTab, lpitem->xTab );
1012 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1016 /* Finish the column (set all items to the largest width found) */
1017 maxX = max( maxX, maxTab + maxTabWidth );
1018 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1020 lpitem->rect.right = maxX;
1021 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1022 lpitem->xTab = maxTab;
1025 lppop->Height = max( lppop->Height, orgY );
1028 lppop->Width = maxX;
1030 /* space for 3d border */
1034 ReleaseDC( 0, hdc );
1038 /***********************************************************************
1039 * MENU_MenuBarCalcSize
1041 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1042 * height is off by 1 pixel which causes lengthy window relocations when
1043 * active document window is maximized/restored.
1045 * Calculate the size of the menu bar.
1047 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1048 LPPOPUPMENU lppop, HWND hwndOwner )
1051 int start, i, orgX, orgY, maxY, helpPos;
1053 if ((lprect == NULL) || (lppop == NULL)) return;
1054 if (lppop->nItems == 0) return;
1055 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1056 lppop->Width = lprect->right - lprect->left;
1058 maxY = lprect->top+1;
1061 lppop->maxBmpSize.cx = 0;
1062 lppop->maxBmpSize.cy = 0;
1063 while (start < lppop->nItems)
1065 lpitem = &lppop->items[start];
1066 orgX = lprect->left;
1069 /* Parse items until line break or end of menu */
1070 for (i = start; i < lppop->nItems; i++, lpitem++)
1072 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1074 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1076 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1077 debug_print_menuitem (" item: ", lpitem, "");
1078 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1080 if (lpitem->rect.right > lprect->right)
1082 if (i != start) break;
1083 else lpitem->rect.right = lprect->right;
1085 maxY = max( maxY, lpitem->rect.bottom );
1086 orgX = lpitem->rect.right;
1089 /* Finish the line (set all items to the largest height found) */
1090 while (start < i) lppop->items[start++].rect.bottom = maxY;
1093 lprect->bottom = maxY;
1094 lppop->Height = lprect->bottom - lprect->top;
1096 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1097 /* the last item (if several lines, only move the last line) */
1098 lpitem = &lppop->items[lppop->nItems-1];
1099 orgY = lpitem->rect.top;
1100 orgX = lprect->right;
1101 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1102 if ( (helpPos==-1) || (helpPos>i) )
1104 if (lpitem->rect.top != orgY) break; /* Other line */
1105 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1106 lpitem->rect.left += orgX - lpitem->rect.right;
1107 lpitem->rect.right = orgX;
1108 orgX = lpitem->rect.left;
1112 /***********************************************************************
1115 * Draw a single menu item.
1117 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1118 UINT height, BOOL menuBar, UINT odaction )
1121 BOOL flat_menu = FALSE;
1124 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1126 if (lpitem->fType & MF_SYSMENU)
1128 if( !IsIconic(hwnd) )
1129 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1133 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1134 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1138 if (lpitem->fState & MF_HILITE)
1140 if(menuBar && !flat_menu) {
1141 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1142 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1144 if(lpitem->fState & MF_GRAYED)
1145 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1147 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1148 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1153 if (lpitem->fState & MF_GRAYED)
1154 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1156 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1157 SetBkColor( hdc, GetSysColor( bkgnd ) );
1160 if (lpitem->fType & MF_OWNERDRAW)
1163 ** Experimentation under Windows reveals that an owner-drawn
1164 ** menu is given the rectangle which includes the space it requested
1165 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1166 ** and a popup-menu arrow. This is the value of lpitem->rect.
1167 ** Windows will leave all drawing to the application except for
1168 ** the popup-menu arrow. Windows always draws that itself, after
1169 ** the menu owner has finished drawing.
1173 dis.CtlType = ODT_MENU;
1175 dis.itemID = lpitem->wID;
1176 dis.itemData = lpitem->dwItemData;
1178 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1179 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1180 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1181 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1182 dis.hwndItem = (HWND)hmenu;
1184 dis.rcItem = lpitem->rect;
1185 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1186 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1187 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1188 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1189 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1190 /* Fall through to draw popup-menu arrow */
1193 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1195 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1197 rect = lpitem->rect;
1199 if (!(lpitem->fType & MF_OWNERDRAW))
1201 if (lpitem->fState & MF_HILITE)
1205 InflateRect (&rect, -1, -1);
1206 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1207 InflateRect (&rect, 1, 1);
1208 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1213 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1215 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1219 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1222 SetBkMode( hdc, TRANSPARENT );
1224 if (!(lpitem->fType & MF_OWNERDRAW))
1228 /* vertical separator */
1229 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1233 rc.bottom = height - 3;
1236 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1237 MoveToEx( hdc, rc.left, rc.top, NULL );
1238 LineTo( hdc, rc.left, rc.bottom );
1239 SelectObject( hdc, oldPen );
1242 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1245 /* horizontal separator */
1246 if (lpitem->fType & MF_SEPARATOR)
1251 rc.top += SEPARATOR_HEIGHT / 2;
1254 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1255 MoveToEx( hdc, rc.left, rc.top, NULL );
1256 LineTo( hdc, rc.right, rc.top );
1257 SelectObject( hdc, oldPen );
1260 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1265 /* helper lines for debugging */
1266 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1267 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1268 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1269 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1275 INT y = rect.top + rect.bottom;
1276 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1277 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1278 UINT arrow_bitmap_width, arrow_bitmap_height;
1281 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1282 arrow_bitmap_width = bmp.bmWidth;
1283 arrow_bitmap_height = bmp.bmHeight;
1285 if (!(lpitem->fType & MF_OWNERDRAW))
1289 if (lpitem->hbmpItem)
1291 POPUPMENU *menu = MENU_GetMenu(hmenu);
1292 if (menu->dwStyle & MNS_CHECKORBMP)
1293 rc.left += menu->maxBmpSize.cx - check_bitmap_width;
1295 rc.left += menu->maxBmpSize.cx;
1297 /* Draw the check mark
1300 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1302 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1303 if (bm) /* we have a custom bitmap */
1305 HDC hdcMem = CreateCompatibleDC( hdc );
1306 SelectObject( hdcMem, bm );
1307 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1308 check_bitmap_width, check_bitmap_height,
1309 hdcMem, 0, 0, SRCCOPY );
1312 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1315 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1316 HDC hdcMem = CreateCompatibleDC( hdc );
1317 SelectObject( hdcMem, bm );
1318 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1319 DrawFrameControl( hdcMem, &r, DFC_MENU,
1320 (lpitem->fType & MFT_RADIOCHECK) ?
1321 DFCS_MENUBULLET : DFCS_MENUCHECK );
1322 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1323 hdcMem, 0, 0, SRCCOPY );
1327 if (lpitem->hbmpItem)
1329 HBITMAP hbm = lpitem->hbmpItem;
1331 if (hbm == HBMMENU_CALLBACK)
1333 DRAWITEMSTRUCT drawItem;
1335 drawItem.CtlType = ODT_MENU;
1337 drawItem.itemID = lpitem->wID;
1338 drawItem.itemAction = odaction;
1339 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1340 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1341 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1342 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1343 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1344 drawItem.hwndItem = (HWND)hmenu;
1346 drawItem.rcItem = lpitem->rect;
1347 drawItem.itemData = lpitem->dwItemData;
1348 /* some applications make this assumption on the DC's origin */
1349 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1350 OffsetRect( &drawItem.rcItem, - lpitem->rect.left, - lpitem->rect.top);
1351 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1352 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1355 MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE);
1360 /* Draw the popup-menu arrow */
1361 if (lpitem->fType & MF_POPUP)
1363 HDC hdcMem = CreateCompatibleDC( hdc );
1364 HBITMAP hOrigBitmap;
1366 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1367 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1368 (y - arrow_bitmap_height) / 2,
1369 arrow_bitmap_width, arrow_bitmap_height,
1370 hdcMem, 0, 0, SRCCOPY );
1371 SelectObject( hdcMem, hOrigBitmap );
1375 rect.left += check_bitmap_width;
1376 rect.right -= arrow_bitmap_width;
1378 else if( lpitem->hbmpItem && !(lpitem->fType & MF_OWNERDRAW))
1379 { /* Draw the bitmap */
1380 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar);
1382 /* Done for owner-drawn */
1383 if (lpitem->fType & MF_OWNERDRAW)
1385 /* process text if present */
1391 UINT uFormat = (menuBar) ?
1392 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1393 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1395 if ( lpitem->fState & MFS_DEFAULT )
1397 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1402 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1403 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1406 for (i = 0; lpitem->text[i]; i++)
1407 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1410 if(lpitem->fState & MF_GRAYED)
1412 if (!(lpitem->fState & MF_HILITE) )
1414 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1415 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1416 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1417 --rect.left; --rect.top; --rect.right; --rect.bottom;
1419 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1422 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1424 /* paint the shortcut text */
1425 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1427 if (lpitem->text[i] == '\t')
1429 rect.left = lpitem->xTab;
1430 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1434 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1437 if(lpitem->fState & MF_GRAYED)
1439 if (!(lpitem->fState & MF_HILITE) )
1441 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1442 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1443 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1444 --rect.left; --rect.top; --rect.right; --rect.bottom;
1446 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1448 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1452 SelectObject (hdc, hfontOld);
1457 /***********************************************************************
1458 * MENU_DrawPopupMenu
1460 * Paint a popup menu.
1462 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1464 HBRUSH hPrevBrush = 0;
1467 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1469 GetClientRect( hwnd, &rect );
1471 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1472 && (SelectObject( hdc, get_menu_font(FALSE))))
1476 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1478 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1482 BOOL flat_menu = FALSE;
1484 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1486 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1488 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1490 /* draw menu items */
1492 menu = MENU_GetMenu( hmenu );
1493 if (menu && menu->nItems)
1498 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1499 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1500 menu->Height, FALSE, ODA_DRAWENTIRE );
1505 SelectObject( hdc, hPrevBrush );
1510 /***********************************************************************
1513 * Paint a menu bar. Returns the height of the menu bar.
1514 * called from [windows/nonclient.c]
1516 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1521 HMENU hMenu = GetMenu(hwnd);
1523 lppop = MENU_GetMenu( hMenu );
1524 if (lppop == NULL || lprect == NULL)
1526 return GetSystemMetrics(SM_CYMENU);
1531 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1533 if (lppop->Height == 0)
1534 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1536 lprect->bottom = lprect->top + lppop->Height;
1538 if (hfontOld) SelectObject( hDC, hfontOld);
1539 return lppop->Height;
1542 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1546 /***********************************************************************
1549 * Display a popup menu.
1551 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1552 INT x, INT y, INT xanchor, INT yanchor )
1557 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1558 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1560 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1561 if (menu->FocusedItem != NO_SELECTED_ITEM)
1563 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1564 menu->FocusedItem = NO_SELECTED_ITEM;
1567 /* store the owner for DrawItem */
1568 menu->hwndOwner = hwndOwner;
1570 MENU_PopupMenuCalcSize( menu, hwndOwner );
1572 /* adjust popup menu pos so that it fits within the desktop */
1574 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1575 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1577 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1580 x -= width - xanchor;
1581 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1582 x = GetSystemMetrics(SM_CXSCREEN) - width;
1586 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1589 y -= height + yanchor;
1590 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1591 y = GetSystemMetrics(SM_CYSCREEN) - height;
1595 /* NOTE: In Windows, top menu popup is not owned. */
1596 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1597 WS_POPUP, x, y, width, height,
1598 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1600 if( !menu->hWnd ) return FALSE;
1601 if (!top_popup) top_popup = menu->hWnd;
1603 /* Display the window */
1605 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1606 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1607 UpdateWindow( menu->hWnd );
1612 /***********************************************************************
1615 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1616 BOOL sendMenuSelect, HMENU topmenu )
1621 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1623 lppop = MENU_GetMenu( hmenu );
1624 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1626 if (lppop->FocusedItem == wIndex) return;
1627 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1628 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1629 if (!top_popup) top_popup = lppop->hWnd;
1631 SelectObject( hdc, get_menu_font(FALSE));
1633 /* Clear previous highlighted item */
1634 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1636 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1637 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1638 lppop->Height, !(lppop->wFlags & MF_POPUP),
1642 /* Highlight new item (if any) */
1643 lppop->FocusedItem = wIndex;
1644 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1646 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1647 lppop->items[wIndex].fState |= MF_HILITE;
1648 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1649 &lppop->items[wIndex], lppop->Height,
1650 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1654 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1655 SendMessageW( hwndOwner, WM_MENUSELECT,
1656 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1657 ip->fType | ip->fState |
1658 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1661 else if (sendMenuSelect) {
1664 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1665 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1666 MENUITEM *ip = &ptm->items[pos];
1667 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1668 ip->fType | ip->fState |
1669 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1673 ReleaseDC( lppop->hWnd, hdc );
1677 /***********************************************************************
1678 * MENU_MoveSelection
1680 * Moves currently selected item according to the offset parameter.
1681 * If there is no selection then it should select the last item if
1682 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1684 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1689 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1691 menu = MENU_GetMenu( hmenu );
1692 if ((!menu) || (!menu->items)) return;
1694 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1696 if( menu->nItems == 1 ) return; else
1697 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1699 if (!(menu->items[i].fType & MF_SEPARATOR))
1701 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1706 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1707 i >= 0 && i < menu->nItems ; i += offset)
1708 if (!(menu->items[i].fType & MF_SEPARATOR))
1710 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1716 /**********************************************************************
1719 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1722 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1725 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1726 TRACE("flags=%x str=%p\n", flags, str);
1728 if (IS_STRING_ITEM(flags))
1730 LPWSTR prevText = item->text;
1733 flags |= MF_SEPARATOR;
1739 /* Item beginning with a backspace is a help item */
1745 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1747 strcpyW( text, str );
1750 item->hbmpItem = NULL;
1751 HeapFree( GetProcessHeap(), 0, prevText );
1753 else if(( flags & MFT_BITMAP)) {
1754 item->hbmpItem = HBITMAP_32(LOWORD(str));
1755 /* setting bitmap clears text */
1756 HeapFree( GetProcessHeap(), 0, item->text );
1760 if (flags & MF_OWNERDRAW)
1761 item->dwItemData = (DWORD_PTR)str;
1763 item->dwItemData = 0;
1765 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1766 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1768 if (flags & MF_POPUP)
1770 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1771 if (menu) menu->wFlags |= MF_POPUP;
1783 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1785 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1786 flags |= MF_POPUP; /* keep popup */
1788 item->fType = flags & TYPE_MASK;
1789 item->fState = (flags & STATE_MASK) &
1790 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1792 /* Don't call SetRectEmpty here! */
1794 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1799 /**********************************************************************
1802 * Insert (allocate) a new item into a menu.
1804 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1809 if (!(menu = MENU_GetMenu(hMenu)))
1812 /* Find where to insert new item */
1814 if (flags & MF_BYPOSITION) {
1815 if (pos > menu->nItems)
1818 if (!MENU_FindItem( &hMenu, &pos, flags ))
1821 if (!(menu = MENU_GetMenu( hMenu )))
1826 /* Create new items array */
1828 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1831 WARN("allocation failed\n" );
1834 if (menu->nItems > 0)
1836 /* Copy the old array into the new one */
1837 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1838 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1839 (menu->nItems-pos)*sizeof(MENUITEM) );
1840 HeapFree( GetProcessHeap(), 0, menu->items );
1842 menu->items = newItems;
1844 memset( &newItems[pos], 0, sizeof(*newItems) );
1845 menu->Height = 0; /* force size recalculate */
1846 return &newItems[pos];
1850 /**********************************************************************
1851 * MENU_ParseResource
1853 * Parse a standard menu resource and add items to the menu.
1854 * Return a pointer to the end of the resource.
1856 * NOTE: flags is equivalent to the mtOption field
1858 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1865 flags = GET_WORD(res);
1866 res += sizeof(WORD);
1867 if (!(flags & MF_POPUP))
1870 res += sizeof(WORD);
1873 if (!unicode) res += strlen(str) + 1;
1874 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1875 if (flags & MF_POPUP)
1877 HMENU hSubMenu = CreatePopupMenu();
1878 if (!hSubMenu) return NULL;
1879 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1881 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
1882 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
1884 else /* Not a popup */
1886 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1887 else AppendMenuW( hMenu, flags, id,
1888 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1890 } while (!(flags & MF_END));
1895 /**********************************************************************
1896 * MENUEX_ParseResource
1898 * Parse an extended menu resource and add items to the menu.
1899 * Return a pointer to the end of the resource.
1901 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1907 mii.cbSize = sizeof(mii);
1908 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1909 mii.fType = GET_DWORD(res);
1910 res += sizeof(DWORD);
1911 mii.fState = GET_DWORD(res);
1912 res += sizeof(DWORD);
1913 mii.wID = GET_DWORD(res);
1914 res += sizeof(DWORD);
1915 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1916 res += sizeof(WORD);
1917 /* Align the text on a word boundary. */
1918 res += (~((UINT_PTR)res - 1)) & 1;
1919 mii.dwTypeData = (LPWSTR) res;
1920 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1921 /* Align the following fields on a dword boundary. */
1922 res += (~((UINT_PTR)res - 1)) & 3;
1924 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1925 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1927 if (resinfo & 1) { /* Pop-up? */
1928 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1929 res += sizeof(DWORD);
1930 mii.hSubMenu = CreatePopupMenu();
1933 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1934 DestroyMenu(mii.hSubMenu);
1937 mii.fMask |= MIIM_SUBMENU;
1938 mii.fType |= MF_POPUP;
1940 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1942 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1943 mii.wID, mii.fType);
1944 mii.fType |= MF_SEPARATOR;
1946 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1947 } while (!(resinfo & MF_END));
1952 /***********************************************************************
1955 * Return the handle of the selected sub-popup menu (if any).
1957 static HMENU MENU_GetSubPopup( HMENU hmenu )
1962 menu = MENU_GetMenu( hmenu );
1964 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1966 item = &menu->items[menu->FocusedItem];
1967 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1968 return item->hSubMenu;
1973 /***********************************************************************
1974 * MENU_HideSubPopups
1976 * Hide the sub-popup menus of this menu.
1978 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1979 BOOL sendMenuSelect )
1981 POPUPMENU *menu = MENU_GetMenu( hmenu );
1983 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1985 if (menu && top_popup)
1991 if (menu->FocusedItem != NO_SELECTED_ITEM)
1993 item = &menu->items[menu->FocusedItem];
1994 if (!(item->fType & MF_POPUP) ||
1995 !(item->fState & MF_MOUSESELECT)) return;
1996 item->fState &= ~MF_MOUSESELECT;
1997 hsubmenu = item->hSubMenu;
2000 submenu = MENU_GetMenu( hsubmenu );
2001 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2002 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2003 DestroyWindow( submenu->hWnd );
2009 /***********************************************************************
2012 * Display the sub-menu of the selected item of this menu.
2013 * Return the handle of the submenu, or hmenu if no submenu to display.
2015 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2016 BOOL selectFirst, UINT wFlags )
2023 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2025 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2027 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2029 item = &menu->items[menu->FocusedItem];
2030 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2033 /* message must be sent before using item,
2034 because nearly everything may be changed by the application ! */
2036 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2037 if (!(wFlags & TPM_NONOTIFY))
2038 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2039 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2041 item = &menu->items[menu->FocusedItem];
2044 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2045 if (!(item->fState & MF_HILITE))
2047 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2048 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2050 SelectObject( hdc, get_menu_font(FALSE));
2052 item->fState |= MF_HILITE;
2053 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2054 ReleaseDC( menu->hWnd, hdc );
2056 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2059 item->fState |= MF_MOUSESELECT;
2061 if (IS_SYSTEM_MENU(menu))
2063 MENU_InitSysMenuPopup(item->hSubMenu,
2064 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2065 GetClassLongW( menu->hWnd, GCL_STYLE));
2067 NC_GetSysPopupPos( menu->hWnd, &rect );
2068 rect.top = rect.bottom;
2069 rect.right = GetSystemMetrics(SM_CXSIZE);
2070 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2074 GetWindowRect( menu->hWnd, &rect );
2075 if (menu->wFlags & MF_POPUP)
2077 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2078 rect.top += item->rect.top;
2079 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2080 rect.bottom = item->rect.top - item->rect.bottom;
2084 rect.left += item->rect.left;
2085 rect.top += item->rect.bottom;
2086 rect.right = item->rect.right - item->rect.left;
2087 rect.bottom = item->rect.bottom - item->rect.top;
2091 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2092 rect.left, rect.top, rect.right, rect.bottom );
2094 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2095 return item->hSubMenu;
2100 /**********************************************************************
2103 HWND MENU_IsMenuActive(void)
2108 /***********************************************************************
2111 * Walks menu chain trying to find a menu pt maps to.
2113 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2115 POPUPMENU *menu = MENU_GetMenu( hMenu );
2116 UINT item = menu->FocusedItem;
2119 /* try subpopup first (if any) */
2120 ret = (item != NO_SELECTED_ITEM &&
2121 (menu->items[item].fType & MF_POPUP) &&
2122 (menu->items[item].fState & MF_MOUSESELECT))
2123 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2125 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2127 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2128 if( menu->wFlags & MF_POPUP )
2130 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2132 else if (ht == HTSYSMENU)
2133 ret = get_win_sys_menu( menu->hWnd );
2134 else if (ht == HTMENU)
2135 ret = GetMenu( menu->hWnd );
2140 /***********************************************************************
2141 * MENU_ExecFocusedItem
2143 * Execute a menu item (for instance when user pressed Enter).
2144 * Return the wID of the executed item. Otherwise, -1 indicating
2145 * that no menu item was executed;
2146 * Have to receive the flags for the TrackPopupMenu options to avoid
2147 * sending unwanted message.
2150 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2153 POPUPMENU *menu = MENU_GetMenu( hMenu );
2155 TRACE("%p hmenu=%p\n", pmt, hMenu);
2157 if (!menu || !menu->nItems ||
2158 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2160 item = &menu->items[menu->FocusedItem];
2162 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2164 if (!(item->fType & MF_POPUP))
2166 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2168 /* If TPM_RETURNCMD is set you return the id, but
2169 do not send a message to the owner */
2170 if(!(wFlags & TPM_RETURNCMD))
2172 if( menu->wFlags & MF_SYSMENU )
2173 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2174 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2176 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2182 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2187 /***********************************************************************
2188 * MENU_SwitchTracking
2190 * Helper function for menu navigation routines.
2192 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2194 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2195 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2197 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2199 if( pmt->hTopMenu != hPtMenu &&
2200 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2202 /* both are top level menus (system and menu-bar) */
2203 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2204 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2205 pmt->hTopMenu = hPtMenu;
2207 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2208 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2212 /***********************************************************************
2215 * Return TRUE if we can go on with menu tracking.
2217 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2219 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2224 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2227 if( IS_SYSTEM_MENU(ptmenu) )
2228 item = ptmenu->items;
2230 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2234 if( ptmenu->FocusedItem != id )
2235 MENU_SwitchTracking( pmt, hPtMenu, id );
2237 /* If the popup menu is not already "popped" */
2238 if(!(item->fState & MF_MOUSESELECT ))
2240 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2245 /* Else the click was on the menu bar, finish the tracking */
2250 /***********************************************************************
2253 * Return the value of MENU_ExecFocusedItem if
2254 * the selected item was not a popup. Else open the popup.
2255 * A -1 return value indicates that we go on with menu tracking.
2258 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2260 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2265 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2268 if( IS_SYSTEM_MENU(ptmenu) )
2269 item = ptmenu->items;
2271 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2273 if( item && (ptmenu->FocusedItem == id ))
2275 if( !(item->fType & MF_POPUP) )
2276 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2278 /* If we are dealing with the top-level menu */
2279 /* and this is a click on an already "popped" item: */
2280 /* Stop the menu tracking and close the opened submenus */
2281 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2284 ptmenu->bTimeToHide = TRUE;
2290 /***********************************************************************
2293 * Return TRUE if we can go on with menu tracking.
2295 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2297 UINT id = NO_SELECTED_ITEM;
2298 POPUPMENU *ptmenu = NULL;
2302 ptmenu = MENU_GetMenu( hPtMenu );
2303 if( IS_SYSTEM_MENU(ptmenu) )
2306 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2309 if( id == NO_SELECTED_ITEM )
2311 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2312 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2315 else if( ptmenu->FocusedItem != id )
2317 MENU_SwitchTracking( pmt, hPtMenu, id );
2318 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2324 /***********************************************************************
2327 static void MENU_SetCapture( HWND hwnd )
2331 SERVER_START_REQ( set_capture_window )
2334 req->flags = CAPTURE_MENU;
2335 if (!wine_server_call_err( req ))
2337 previous = reply->previous;
2338 hwnd = reply->full_handle;
2343 if (previous && previous != hwnd)
2344 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2348 /***********************************************************************
2351 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2353 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2355 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2357 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2358 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2360 MDINEXTMENU next_menu;
2365 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2366 next_menu.hmenuNext = 0;
2367 next_menu.hwndNext = 0;
2368 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2370 TRACE("%p [%p] -> %p [%p]\n",
2371 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2373 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2375 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2376 hNewWnd = pmt->hOwnerWnd;
2377 if( IS_SYSTEM_MENU(menu) )
2379 /* switch to the menu bar */
2381 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2385 menu = MENU_GetMenu( hNewMenu );
2386 id = menu->nItems - 1;
2389 else if (style & WS_SYSMENU )
2391 /* switch to the system menu */
2392 hNewMenu = get_win_sys_menu( hNewWnd );
2396 else /* application returned a new menu to switch to */
2398 hNewMenu = next_menu.hmenuNext;
2399 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2401 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2403 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2405 if (style & WS_SYSMENU &&
2406 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2408 /* get the real system menu */
2409 hNewMenu = get_win_sys_menu(hNewWnd);
2411 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2413 /* FIXME: Not sure what to do here;
2414 * perhaps try to track hNewMenu as a popup? */
2416 TRACE(" -- got confused.\n");
2423 if( hNewMenu != pmt->hTopMenu )
2425 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2427 if( pmt->hCurrentMenu != pmt->hTopMenu )
2428 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2431 if( hNewWnd != pmt->hOwnerWnd )
2433 pmt->hOwnerWnd = hNewWnd;
2434 MENU_SetCapture( pmt->hOwnerWnd );
2437 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2438 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2445 /***********************************************************************
2448 * The idea is not to show the popup if the next input message is
2449 * going to hide it anyway.
2451 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2455 msg.hwnd = pmt->hOwnerWnd;
2457 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2458 pmt->trackFlags |= TF_SKIPREMOVE;
2463 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2464 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2466 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2467 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2468 if( msg.message == WM_KEYDOWN &&
2469 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2471 pmt->trackFlags |= TF_SUSPENDPOPUP;
2478 /* failures go through this */
2479 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2483 /***********************************************************************
2486 * Handle a VK_ESCAPE key event in a menu.
2488 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2490 BOOL bEndMenu = TRUE;
2492 if (pmt->hCurrentMenu != pmt->hTopMenu)
2494 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2496 if (menu->wFlags & MF_POPUP)
2498 HMENU hmenutmp, hmenuprev;
2500 hmenuprev = hmenutmp = pmt->hTopMenu;
2502 /* close topmost popup */
2503 while (hmenutmp != pmt->hCurrentMenu)
2505 hmenuprev = hmenutmp;
2506 hmenutmp = MENU_GetSubPopup( hmenuprev );
2509 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2510 pmt->hCurrentMenu = hmenuprev;
2518 /***********************************************************************
2521 * Handle a VK_LEFT key event in a menu.
2523 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2526 HMENU hmenutmp, hmenuprev;
2529 hmenuprev = hmenutmp = pmt->hTopMenu;
2530 menu = MENU_GetMenu( hmenutmp );
2532 /* Try to move 1 column left (if possible) */
2533 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2534 NO_SELECTED_ITEM ) {
2536 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2541 /* close topmost popup */
2542 while (hmenutmp != pmt->hCurrentMenu)
2544 hmenuprev = hmenutmp;
2545 hmenutmp = MENU_GetSubPopup( hmenuprev );
2548 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2549 pmt->hCurrentMenu = hmenuprev;
2551 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2553 /* move menu bar selection if no more popups are left */
2555 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2556 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2558 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2560 /* A sublevel menu was displayed - display the next one
2561 * unless there is another displacement coming up */
2563 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2564 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2565 pmt->hTopMenu, TRUE, wFlags);
2571 /***********************************************************************
2574 * Handle a VK_RIGHT key event in a menu.
2576 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2579 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2582 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2584 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2585 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2587 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2589 /* If already displaying a popup, try to display sub-popup */
2591 hmenutmp = pmt->hCurrentMenu;
2592 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2594 /* if subpopup was displayed then we are done */
2595 if (hmenutmp != pmt->hCurrentMenu) return;
2598 /* Check to see if there's another column */
2599 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2600 NO_SELECTED_ITEM ) {
2601 TRACE("Going to %d.\n", nextcol );
2602 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2607 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2609 if( pmt->hCurrentMenu != pmt->hTopMenu )
2611 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2612 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2613 } else hmenutmp = 0;
2615 /* try to move to the next item */
2616 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2617 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2619 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2620 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2621 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2622 pmt->hTopMenu, TRUE, wFlags);
2626 /***********************************************************************
2629 * Menu tracking code.
2631 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2632 HWND hwnd, const RECT *lprect )
2637 INT executedMenuId = -1;
2639 BOOL enterIdleSent = FALSE;
2642 mt.hCurrentMenu = hmenu;
2643 mt.hTopMenu = hmenu;
2644 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2648 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2649 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2652 if (!(menu = MENU_GetMenu( hmenu )))
2654 WARN("Invalid menu handle %p\n", hmenu);
2655 SetLastError(ERROR_INVALID_MENU_HANDLE);
2659 if (wFlags & TPM_BUTTONDOWN)
2661 /* Get the result in order to start the tracking or not */
2662 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2663 fEndMenu = !fRemove;
2666 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2668 MENU_SetCapture( mt.hOwnerWnd );
2672 menu = MENU_GetMenu( mt.hCurrentMenu );
2673 if (!menu) /* sometimes happens if I do a window manager close */
2676 /* we have to keep the message in the queue until it's
2677 * clear that menu loop is not over yet. */
2681 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2683 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2684 /* remove the message from the queue */
2685 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2691 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2692 enterIdleSent = TRUE;
2693 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2699 /* check if EndMenu() tried to cancel us, by posting this message */
2700 if(msg.message == WM_CANCELMODE)
2702 /* we are now out of the loop */
2705 /* remove the message from the queue */
2706 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2708 /* break out of internal loop, ala ESCAPE */
2712 TranslateMessage( &msg );
2715 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2716 enterIdleSent=FALSE;
2719 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2722 * Use the mouse coordinates in lParam instead of those in the MSG
2723 * struct to properly handle synthetic messages. They are already
2724 * in screen coordinates.
2726 mt.pt.x = (short)LOWORD(msg.lParam);
2727 mt.pt.y = (short)HIWORD(msg.lParam);
2729 /* Find a menu for this mouse event */
2730 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2734 /* no WM_NC... messages in captured state */
2736 case WM_RBUTTONDBLCLK:
2737 case WM_RBUTTONDOWN:
2738 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2740 case WM_LBUTTONDBLCLK:
2741 case WM_LBUTTONDOWN:
2742 /* If the message belongs to the menu, removes it from the queue */
2743 /* Else, end menu tracking */
2744 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2745 fEndMenu = !fRemove;
2749 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2752 /* Check if a menu was selected by the mouse */
2755 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2757 /* End the loop if executedMenuId is an item ID */
2758 /* or if the job was done (executedMenuId = 0). */
2759 fEndMenu = fRemove = (executedMenuId != -1);
2761 /* No menu was selected by the mouse */
2762 /* if the function was called by TrackPopupMenu, continue
2763 with the menu tracking. If not, stop it */
2765 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2770 /* the selected menu item must be changed every time */
2771 /* the mouse moves. */
2774 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2776 } /* switch(msg.message) - mouse */
2778 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2780 fRemove = TRUE; /* Keyboard messages are always removed */
2793 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2794 NO_SELECTED_ITEM, FALSE, 0 );
2797 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2798 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2801 case VK_DOWN: /* If on menu bar, pull-down the menu */
2803 menu = MENU_GetMenu( mt.hCurrentMenu );
2804 if (!(menu->wFlags & MF_POPUP))
2805 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2806 else /* otherwise try to move selection */
2807 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2811 MENU_KeyLeft( &mt, wFlags );
2815 MENU_KeyRight( &mt, wFlags );
2819 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2825 hi.cbSize = sizeof(HELPINFO);
2826 hi.iContextType = HELPINFO_MENUITEM;
2827 if (menu->FocusedItem == NO_SELECTED_ITEM)
2830 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2831 hi.hItemHandle = hmenu;
2832 hi.dwContextId = menu->dwContextHelpID;
2833 hi.MousePos = msg.pt;
2834 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2841 break; /* WM_KEYDOWN */
2848 if (msg.wParam == '\r' || msg.wParam == ' ')
2850 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2851 fEndMenu = (executedMenuId != -1);
2856 /* Hack to avoid control chars. */
2857 /* We will find a better way real soon... */
2858 if (msg.wParam < 32) break;
2860 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2861 LOWORD(msg.wParam), FALSE );
2862 if (pos == (UINT)-2) fEndMenu = TRUE;
2863 else if (pos == (UINT)-1) MessageBeep(0);
2866 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2868 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2869 fEndMenu = (executedMenuId != -1);
2873 } /* switch(msg.message) - kbd */
2877 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2878 DispatchMessageW( &msg );
2882 if (!fEndMenu) fRemove = TRUE;
2884 /* finally remove message from the queue */
2886 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2887 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2888 else mt.trackFlags &= ~TF_SKIPREMOVE;
2891 MENU_SetCapture(0); /* release the capture */
2893 /* If dropdown is still painted and the close box is clicked on
2894 then the menu will be destroyed as part of the DispatchMessage above.
2895 This will then invalidate the menu handle in mt.hTopMenu. We should
2896 check for this first. */
2897 if( IsMenu( mt.hTopMenu ) )
2899 menu = MENU_GetMenu( mt.hTopMenu );
2901 if( IsWindow( mt.hOwnerWnd ) )
2903 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2905 if (menu && (menu->wFlags & MF_POPUP))
2907 DestroyWindow( menu->hWnd );
2910 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2911 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2914 /* Reset the variable for hiding menu */
2915 if( menu ) menu->bTimeToHide = FALSE;
2918 /* The return value is only used by TrackPopupMenu */
2919 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2920 if (executedMenuId == -1) executedMenuId = 0;
2921 return executedMenuId;
2924 /***********************************************************************
2927 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2931 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2935 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2936 if (!(wFlags & TPM_NONOTIFY))
2937 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2939 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2941 if (!(wFlags & TPM_NONOTIFY))
2943 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2944 /* If an app changed/recreated menu bar entries in WM_INITMENU
2945 * menu sizes will be recalculated once the menu created/shown.
2949 /* This makes the menus of applications built with Delphi work.
2950 * It also enables menus to be displayed in more than one window,
2951 * but there are some bugs left that need to be fixed in this case.
2953 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2957 /***********************************************************************
2960 static BOOL MENU_ExitTracking(HWND hWnd)
2962 TRACE("hwnd=%p\n", hWnd);
2964 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2970 /***********************************************************************
2971 * MENU_TrackMouseMenuBar
2973 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2975 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2977 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2978 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2980 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
2984 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2985 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2986 MENU_ExitTracking(hWnd);
2991 /***********************************************************************
2992 * MENU_TrackKbdMenuBar
2994 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2996 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
2998 UINT uItem = NO_SELECTED_ITEM;
3000 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3002 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3004 /* find window that has a menu */
3006 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3007 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3009 /* check if we have to track a system menu */
3011 hTrackMenu = GetMenu( hwnd );
3012 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3014 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3015 hTrackMenu = get_win_sys_menu( hwnd );
3017 wParam |= HTSYSMENU; /* prevent item lookup */
3020 if (!IsMenu( hTrackMenu )) return;
3022 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3024 if( wChar && wChar != ' ' )
3026 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3027 if ( uItem >= (UINT)(-2) )
3029 if( uItem == (UINT)(-1) ) MessageBeep(0);
3030 /* schedule end of menu tracking */
3031 wFlags |= TF_ENDMENU;
3036 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3038 if (wParam & HTSYSMENU)
3040 /* prevent sysmenu activation for managed windows on Alt down/up */
3041 if (GetPropA( hwnd, "__wine_x11_managed" ))
3042 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3046 if( uItem == NO_SELECTED_ITEM )
3047 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3049 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3053 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3054 MENU_ExitTracking( hwnd );
3058 /**********************************************************************
3059 * TrackPopupMenu (USER32.@)
3061 * Like the win32 API, the function return the command ID only if the
3062 * flag TPM_RETURNCMD is on.
3065 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3066 INT nReserved, HWND hWnd, const RECT *lpRect )
3070 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3072 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3073 if (!(wFlags & TPM_NONOTIFY))
3074 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3076 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3077 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3078 MENU_ExitTracking(hWnd);
3083 /**********************************************************************
3084 * TrackPopupMenuEx (USER32.@)
3086 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3087 HWND hWnd, LPTPMPARAMS lpTpm )
3089 FIXME("not fully implemented\n" );
3090 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3091 lpTpm ? &lpTpm->rcExclude : NULL );
3094 /***********************************************************************
3097 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3099 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3101 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3107 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3108 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3112 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3113 return MA_NOACTIVATE;
3118 BeginPaint( hwnd, &ps );
3119 MENU_DrawPopupMenu( hwnd, ps.hdc,
3120 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3121 EndPaint( hwnd, &ps );
3128 /* zero out global pointer in case resident popup window was destroyed. */
3129 if (hwnd == top_popup) top_popup = 0;
3136 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3139 SetWindowLongPtrW( hwnd, 0, 0 );
3142 case MM_SETMENUHANDLE:
3143 SetWindowLongPtrW( hwnd, 0, wParam );
3146 case MM_GETMENUHANDLE:
3147 return GetWindowLongPtrW( hwnd, 0 );
3150 return DefWindowProcW( hwnd, message, wParam, lParam );
3156 /***********************************************************************
3157 * MENU_GetMenuBarHeight
3159 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3161 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3162 INT orgX, INT orgY )
3168 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3170 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3172 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3173 SelectObject( hdc, get_menu_font(FALSE));
3174 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3175 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3176 ReleaseDC( hwnd, hdc );
3177 return lppop->Height;
3181 /*******************************************************************
3182 * ChangeMenuA (USER32.@)
3184 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3185 UINT id, UINT flags )
3187 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3188 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3190 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3191 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3193 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3194 flags & MF_BYPOSITION ? pos : id,
3195 flags & ~MF_REMOVE );
3196 /* Default: MF_INSERT */
3197 return InsertMenuA( hMenu, pos, flags, id, data );
3201 /*******************************************************************
3202 * ChangeMenuW (USER32.@)
3204 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3205 UINT id, UINT flags )
3207 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3208 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3210 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3211 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3213 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3214 flags & MF_BYPOSITION ? pos : id,
3215 flags & ~MF_REMOVE );
3216 /* Default: MF_INSERT */
3217 return InsertMenuW( hMenu, pos, flags, id, data );
3221 /*******************************************************************
3222 * CheckMenuItem (USER32.@)
3224 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3229 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3230 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3231 ret = item->fState & MF_CHECKED;
3232 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3233 else item->fState &= ~MF_CHECKED;
3238 /**********************************************************************
3239 * EnableMenuItem (USER32.@)
3241 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3247 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3249 /* Get the Popupmenu to access the owner menu */
3250 if (!(menu = MENU_GetMenu(hMenu)))
3253 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3256 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3257 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3259 /* If the close item in the system menu change update the close button */
3260 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3262 if (menu->hSysMenuOwner != 0)
3265 POPUPMENU* parentMenu;
3267 /* Get the parent menu to access*/
3268 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3271 /* Refresh the frame to reflect the change */
3272 GetWindowRect(parentMenu->hWnd, &rc);
3273 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3275 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3283 /*******************************************************************
3284 * GetMenuStringA (USER32.@)
3286 INT WINAPI GetMenuStringA(
3287 HMENU hMenu, /* [in] menuhandle */
3288 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3289 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3290 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3291 UINT wFlags /* [in] MF_ flags */
3295 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3296 if (str && nMaxSiz) str[0] = '\0';
3297 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3298 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3301 if (!item->text) return 0;
3302 if (!str || !nMaxSiz) return strlenW(item->text);
3303 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3305 TRACE("returning '%s'\n", str );
3310 /*******************************************************************
3311 * GetMenuStringW (USER32.@)
3313 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3314 LPWSTR str, INT nMaxSiz, UINT wFlags )
3318 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3319 if (str && nMaxSiz) str[0] = '\0';
3320 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3321 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3324 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3325 if( !(item->text)) {
3329 lstrcpynW( str, item->text, nMaxSiz );
3330 return strlenW(str);
3334 /**********************************************************************
3335 * HiliteMenuItem (USER32.@)
3337 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3341 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3342 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3343 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3344 if (menu->FocusedItem == wItemID) return TRUE;
3345 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3346 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3351 /**********************************************************************
3352 * GetMenuState (USER32.@)
3354 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3357 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3358 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3359 debug_print_menuitem (" item: ", item, "");
3360 if (item->fType & MF_POPUP)
3362 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3363 if (!menu) return -1;
3364 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3368 /* We used to (from way back then) mask the result to 0xff. */
3369 /* I don't know why and it seems wrong as the documented */
3370 /* return flag MF_SEPARATOR is outside that mask. */
3371 return (item->fType | item->fState);
3376 /**********************************************************************
3377 * GetMenuItemCount (USER32.@)
3379 INT WINAPI GetMenuItemCount( HMENU hMenu )
3381 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3382 if (!menu) return -1;
3383 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3384 return menu->nItems;
3388 /**********************************************************************
3389 * GetMenuItemID (USER32.@)
3391 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3395 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3396 if (lpmi->fType & MF_POPUP) return -1;
3402 /*******************************************************************
3403 * InsertMenuW (USER32.@)
3405 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3406 UINT_PTR id, LPCWSTR str )
3410 if (IS_STRING_ITEM(flags) && str)
3411 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3412 hMenu, pos, flags, id, debugstr_w(str) );
3413 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3414 hMenu, pos, flags, id, str );
3416 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3418 if (!(MENU_SetItemData( item, flags, id, str )))
3420 RemoveMenu( hMenu, pos, flags );
3424 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3425 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3427 item->hCheckBit = item->hUnCheckBit = 0;
3432 /*******************************************************************
3433 * InsertMenuA (USER32.@)
3435 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3436 UINT_PTR id, LPCSTR str )
3440 if (IS_STRING_ITEM(flags) && str)
3442 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3443 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3446 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3447 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3448 HeapFree( GetProcessHeap(), 0, newstr );
3452 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3456 /*******************************************************************
3457 * AppendMenuA (USER32.@)
3459 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3460 UINT_PTR id, LPCSTR data )
3462 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3466 /*******************************************************************
3467 * AppendMenuW (USER32.@)
3469 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3470 UINT_PTR id, LPCWSTR data )
3472 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3476 /**********************************************************************
3477 * RemoveMenu (USER32.@)
3479 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3484 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3485 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3486 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3490 MENU_FreeItemData( item );
3492 if (--menu->nItems == 0)
3494 HeapFree( GetProcessHeap(), 0, menu->items );
3499 while(nPos < menu->nItems)
3505 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3506 menu->nItems * sizeof(MENUITEM) );
3512 /**********************************************************************
3513 * DeleteMenu (USER32.@)
3515 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3517 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3518 if (!item) return FALSE;
3519 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3520 /* nPos is now the position of the item */
3521 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3526 /*******************************************************************
3527 * ModifyMenuW (USER32.@)
3529 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3530 UINT_PTR id, LPCWSTR str )
3534 if (IS_STRING_ITEM(flags))
3535 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3537 TRACE("%p %d %04x %04x %p\n", hMenu, pos, flags, id, str );
3539 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3540 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3541 return MENU_SetItemData( item, flags, id, str );
3545 /*******************************************************************
3546 * ModifyMenuA (USER32.@)
3548 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3549 UINT_PTR id, LPCSTR str )
3553 if (IS_STRING_ITEM(flags) && str)
3555 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3556 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3559 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3560 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3561 HeapFree( GetProcessHeap(), 0, newstr );
3565 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3569 /**********************************************************************
3570 * CreatePopupMenu (USER32.@)
3572 HMENU WINAPI CreatePopupMenu(void)
3577 if (!(hmenu = CreateMenu())) return 0;
3578 menu = MENU_GetMenu( hmenu );
3579 menu->wFlags |= MF_POPUP;
3580 menu->bTimeToHide = FALSE;
3585 /**********************************************************************
3586 * GetMenuCheckMarkDimensions (USER.417)
3587 * GetMenuCheckMarkDimensions (USER32.@)
3589 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3591 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3595 /**********************************************************************
3596 * SetMenuItemBitmaps (USER32.@)
3598 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3599 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3602 TRACE("(%p, %04x, %04x, %p, %p)\n",
3603 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3604 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3606 if (!hNewCheck && !hNewUnCheck)
3608 item->fState &= ~MF_USECHECKBITMAPS;
3610 else /* Install new bitmaps */
3612 item->hCheckBit = hNewCheck;
3613 item->hUnCheckBit = hNewUnCheck;
3614 item->fState |= MF_USECHECKBITMAPS;
3620 /**********************************************************************
3621 * CreateMenu (USER32.@)
3623 HMENU WINAPI CreateMenu(void)
3627 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3628 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3630 ZeroMemory(menu, sizeof(POPUPMENU));
3631 menu->wMagic = MENU_MAGIC;
3632 menu->FocusedItem = NO_SELECTED_ITEM;
3633 menu->bTimeToHide = FALSE;
3635 TRACE("return %p\n", hMenu );
3641 /**********************************************************************
3642 * DestroyMenu (USER32.@)
3644 BOOL WINAPI DestroyMenu( HMENU hMenu )
3646 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3648 TRACE("(%p)\n", hMenu);
3651 if (!lppop) return FALSE;
3653 lppop->wMagic = 0; /* Mark it as destroyed */
3655 /* DestroyMenu should not destroy system menu popup owner */
3656 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3658 DestroyWindow( lppop->hWnd );
3662 if (lppop->items) /* recursively destroy submenus */
3665 MENUITEM *item = lppop->items;
3666 for (i = lppop->nItems; i > 0; i--, item++)
3668 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3669 MENU_FreeItemData( item );
3671 HeapFree( GetProcessHeap(), 0, lppop->items );
3673 USER_HEAP_FREE( hMenu );
3678 /**********************************************************************
3679 * GetSystemMenu (USER32.@)
3681 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3683 WND *wndPtr = WIN_GetPtr( hWnd );
3686 if (wndPtr == WND_DESKTOP) return 0;
3687 if (wndPtr == WND_OTHER_PROCESS)
3689 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3693 if (wndPtr->hSysMenu && bRevert)
3695 DestroyMenu(wndPtr->hSysMenu);
3696 wndPtr->hSysMenu = 0;
3699 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3700 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
3702 if( wndPtr->hSysMenu )
3705 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3707 /* Store the dummy sysmenu handle to facilitate the refresh */
3708 /* of the close button if the SC_CLOSE item change */
3709 menu = MENU_GetMenu(retvalue);
3711 menu->hSysMenuOwner = wndPtr->hSysMenu;
3713 WIN_ReleasePtr( wndPtr );
3715 return bRevert ? 0 : retvalue;
3719 /*******************************************************************
3720 * SetSystemMenu (USER32.@)
3722 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3724 WND *wndPtr = WIN_GetPtr( hwnd );
3726 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3728 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3729 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3730 WIN_ReleasePtr( wndPtr );
3737 /**********************************************************************
3738 * GetMenu (USER32.@)
3740 HMENU WINAPI GetMenu( HWND hWnd )
3742 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3743 TRACE("for %p returning %p\n", hWnd, retvalue);
3747 /**********************************************************************
3748 * GetMenuBarInfo (USER32.@)
3750 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
3752 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
3756 /**********************************************************************
3759 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3760 * SetWindowPos call that would result if SetMenu were called directly.
3762 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3764 TRACE("(%p, %p);\n", hWnd, hMenu);
3766 if (hMenu && !IsMenu(hMenu))
3768 WARN("hMenu %p is not a menu handle\n", hMenu);
3769 SetLastError(ERROR_INVALID_MENU_HANDLE);
3772 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3775 hWnd = WIN_GetFullHandle( hWnd );
3776 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3782 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3784 lpmenu->hWnd = hWnd;
3785 lpmenu->Height = 0; /* Make sure we recalculate the size */
3787 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3792 /**********************************************************************
3793 * SetMenu (USER32.@)
3795 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3797 if(!MENU_SetMenu(hWnd, hMenu))
3800 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3801 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3806 /**********************************************************************
3807 * GetSubMenu (USER32.@)
3809 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3813 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
3814 if (!(lpmi->fType & MF_POPUP)) return 0;
3815 return lpmi->hSubMenu;
3819 /**********************************************************************
3820 * DrawMenuBar (USER32.@)
3822 BOOL WINAPI DrawMenuBar( HWND hWnd )
3825 HMENU hMenu = GetMenu(hWnd);
3827 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3829 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3831 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3832 lppop->hwndOwner = hWnd;
3833 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3834 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3838 /***********************************************************************
3839 * DrawMenuBarTemp (USER32.@)
3843 * called by W98SE desk.cpl Control Panel Applet
3845 * Not 100% sure about the param names, but close.
3847 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3852 BOOL flat_menu = FALSE;
3854 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
3857 hMenu = GetMenu(hwnd);
3860 hFont = get_menu_font(FALSE);
3862 lppop = MENU_GetMenu( hMenu );
3863 if (lppop == NULL || lprect == NULL)
3865 retvalue = GetSystemMetrics(SM_CYMENU);
3869 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3871 hfontOld = SelectObject( hDC, hFont);
3873 if (lppop->Height == 0)
3874 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3876 lprect->bottom = lprect->top + lppop->Height;
3878 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
3880 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3881 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3882 LineTo( hDC, lprect->right, lprect->bottom );
3884 if (lppop->nItems == 0)
3886 retvalue = GetSystemMetrics(SM_CYMENU);
3890 for (i = 0; i < lppop->nItems; i++)
3892 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3893 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3895 retvalue = lppop->Height;
3898 if (hfontOld) SelectObject (hDC, hfontOld);
3902 /***********************************************************************
3903 * EndMenu (USER.187)
3904 * EndMenu (USER32.@)
3906 void WINAPI EndMenu(void)
3908 /* if we are in the menu code, and it is active */
3909 if (!fEndMenu && top_popup)
3911 /* terminate the menu handling code */
3914 /* needs to be posted to wakeup the internal menu handler */
3915 /* which will now terminate the menu, in the event that */
3916 /* the main window was minimized, or lost focus, so we */
3917 /* don't end up with an orphaned menu */
3918 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3923 /***********************************************************************
3924 * LookupMenuHandle (USER.217)
3926 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3928 HMENU hmenu32 = HMENU_32(hmenu);
3930 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3931 else return HMENU_16(hmenu32);
3935 /**********************************************************************
3936 * LoadMenu (USER.150)
3938 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3944 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3945 if (!name) return 0;
3947 instance = GetExePtr( instance );
3948 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3949 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3950 hMenu = LoadMenuIndirect16(LockResource16(handle));
3951 FreeResource16( handle );
3956 /*****************************************************************
3957 * LoadMenuA (USER32.@)
3959 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3961 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3962 if (!hrsrc) return 0;
3963 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3967 /*****************************************************************
3968 * LoadMenuW (USER32.@)
3970 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3972 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3973 if (!hrsrc) return 0;
3974 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3978 /**********************************************************************
3979 * LoadMenuIndirect (USER.220)
3981 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3984 WORD version, offset;
3985 LPCSTR p = (LPCSTR)template;
3987 TRACE("(%p)\n", template );
3988 version = GET_WORD(p);
3992 WARN("version must be 0 for Win16\n" );
3995 offset = GET_WORD(p);
3996 p += sizeof(WORD) + offset;
3997 if (!(hMenu = CreateMenu())) return 0;
3998 if (!MENU_ParseResource( p, hMenu, FALSE ))
4000 DestroyMenu( hMenu );
4003 return HMENU_16(hMenu);
4007 /**********************************************************************
4008 * LoadMenuIndirectW (USER32.@)
4010 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4013 WORD version, offset;
4014 LPCSTR p = (LPCSTR)template;
4016 version = GET_WORD(p);
4018 TRACE("%p, ver %d\n", template, version );
4021 case 0: /* standard format is version of 0 */
4022 offset = GET_WORD(p);
4023 p += sizeof(WORD) + offset;
4024 if (!(hMenu = CreateMenu())) return 0;
4025 if (!MENU_ParseResource( p, hMenu, TRUE ))
4027 DestroyMenu( hMenu );
4031 case 1: /* extended format is version of 1 */
4032 offset = GET_WORD(p);
4033 p += sizeof(WORD) + offset;
4034 if (!(hMenu = CreateMenu())) return 0;
4035 if (!MENUEX_ParseResource( p, hMenu))
4037 DestroyMenu( hMenu );
4042 ERR("version %d not supported.\n", version);
4048 /**********************************************************************
4049 * LoadMenuIndirectA (USER32.@)
4051 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4053 return LoadMenuIndirectW( template );
4057 /**********************************************************************
4060 BOOL WINAPI IsMenu(HMENU hmenu)
4062 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4063 return menu != NULL;
4066 /**********************************************************************
4067 * GetMenuItemInfo_common
4070 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4071 LPMENUITEMINFOW lpmii, BOOL unicode)
4073 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4075 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4080 if( lpmii->fMask & MIIM_TYPE) {
4081 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4082 SetLastError( ERROR_INVALID_PARAMETER);
4085 lpmii->fType = menu->fType & ~MF_POPUP;
4086 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4087 lpmii->hbmpItem = menu->hbmpItem;
4088 if( lpmii->fType & MFT_BITMAP) {
4089 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4091 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4092 lpmii->dwTypeData = 0;
4097 /* copy the text string */
4098 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4100 if(lpmii->dwTypeData && lpmii->cch) {
4103 *((WCHAR *)lpmii->dwTypeData) = 0;
4105 *((CHAR *)lpmii->dwTypeData) = 0;
4111 len = strlenW(menu->text);
4112 if(lpmii->dwTypeData && lpmii->cch)
4113 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4117 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4118 0, NULL, NULL ) - 1;
4119 if(lpmii->dwTypeData && lpmii->cch)
4120 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4121 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4122 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4124 /* if we've copied a substring we return its length */
4125 if(lpmii->dwTypeData && lpmii->cch)
4126 if (lpmii->cch <= len + 1)
4130 else /* return length of string */
4135 if (lpmii->fMask & MIIM_FTYPE)
4136 lpmii->fType = menu->fType & ~MF_POPUP;
4138 if (lpmii->fMask & MIIM_BITMAP)
4139 lpmii->hbmpItem = menu->hbmpItem;
4141 if (lpmii->fMask & MIIM_STATE)
4142 lpmii->fState = menu->fState;
4144 if (lpmii->fMask & MIIM_ID)
4145 lpmii->wID = menu->wID;
4147 if (lpmii->fMask & MIIM_SUBMENU)
4148 lpmii->hSubMenu = menu->hSubMenu;
4150 lpmii->hSubMenu = 0; /* hSubMenu is always cleared */
4152 if (lpmii->fMask & MIIM_CHECKMARKS) {
4153 lpmii->hbmpChecked = menu->hCheckBit;
4154 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4156 if (lpmii->fMask & MIIM_DATA)
4157 lpmii->dwItemData = menu->dwItemData;
4162 /**********************************************************************
4163 * GetMenuItemInfoA (USER32.@)
4165 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4166 LPMENUITEMINFOA lpmii)
4170 if( lpmii->cbSize != sizeof( mii) &&
4171 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4172 SetLastError( ERROR_INVALID_PARAMETER);
4175 memcpy( &mii, lpmii, lpmii->cbSize);
4176 mii.cbSize = sizeof( mii);
4177 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4178 (LPMENUITEMINFOW)&mii, FALSE);
4179 mii.cbSize = lpmii->cbSize;
4180 memcpy( lpmii, &mii, mii.cbSize);
4184 /**********************************************************************
4185 * GetMenuItemInfoW (USER32.@)
4187 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4188 LPMENUITEMINFOW lpmii)
4192 if( lpmii->cbSize != sizeof( mii) &&
4193 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4194 SetLastError( ERROR_INVALID_PARAMETER);
4197 memcpy( &mii, lpmii, lpmii->cbSize);
4198 mii.cbSize = sizeof( mii);
4199 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4200 mii.cbSize = lpmii->cbSize;
4201 memcpy( lpmii, &mii, mii.cbSize);
4206 /* set a menu item text from a ASCII or Unicode string */
4207 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4213 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4214 strcpyW( menu->text, text );
4218 LPCSTR str = (LPCSTR)text;
4219 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4220 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4221 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4226 /**********************************************************************
4227 * SetMenuItemInfo_common
4230 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4231 const MENUITEMINFOW *lpmii,
4234 if (!menu) return FALSE;
4236 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4238 if (lpmii->fMask & MIIM_TYPE ) {
4239 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4240 SetLastError( ERROR_INVALID_PARAMETER);
4243 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4244 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4245 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4247 if (IS_STRING_ITEM(menu->fType)) {
4248 HeapFree(GetProcessHeap(), 0, menu->text);
4249 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4250 } else if( (menu->fType) & MFT_BITMAP)
4251 menu->hbmpItem = (HBITMAP)lpmii->dwTypeData;
4254 if (lpmii->fMask & MIIM_FTYPE ) {
4255 if(( lpmii->fType & MFT_BITMAP)) {
4256 SetLastError( ERROR_INVALID_PARAMETER);
4259 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4260 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4262 if (lpmii->fMask & MIIM_STRING ) {
4263 /* free the string when used */
4264 HeapFree(GetProcessHeap(), 0, menu->text);
4265 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4268 if (lpmii->fMask & MIIM_STATE)
4270 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4271 menu->fState = lpmii->fState;
4274 if (lpmii->fMask & MIIM_ID)
4275 menu->wID = lpmii->wID;
4277 if (lpmii->fMask & MIIM_SUBMENU) {
4278 menu->hSubMenu = lpmii->hSubMenu;
4279 if (menu->hSubMenu) {
4280 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4282 subMenu->wFlags |= MF_POPUP;
4283 menu->fType |= MF_POPUP;
4286 SetLastError( ERROR_INVALID_PARAMETER);
4291 menu->fType &= ~MF_POPUP;
4294 if (lpmii->fMask & MIIM_CHECKMARKS)
4296 if (lpmii->fType & MFT_RADIOCHECK)
4297 menu->fType |= MFT_RADIOCHECK;
4299 menu->hCheckBit = lpmii->hbmpChecked;
4300 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4302 if (lpmii->fMask & MIIM_DATA)
4303 menu->dwItemData = lpmii->dwItemData;
4305 if (lpmii->fMask & MIIM_BITMAP)
4306 menu->hbmpItem = lpmii->hbmpItem;
4308 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4309 menu->fType |= MFT_SEPARATOR;
4311 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4315 /**********************************************************************
4316 * SetMenuItemInfoA (USER32.@)
4318 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4319 const MENUITEMINFOA *lpmii)
4322 if( lpmii->cbSize != sizeof( mii) &&
4323 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4324 SetLastError( ERROR_INVALID_PARAMETER);
4327 memcpy( &mii, lpmii, lpmii->cbSize);
4328 if( lpmii->cbSize != sizeof( mii)) {
4329 mii.cbSize = sizeof( mii);
4330 mii.hbmpItem = NULL;
4332 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4333 (const MENUITEMINFOW *)&mii, FALSE);
4336 /**********************************************************************
4337 * SetMenuItemInfoW (USER32.@)
4339 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4340 const MENUITEMINFOW *lpmii)
4343 if( lpmii->cbSize != sizeof( mii) &&
4344 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4345 SetLastError( ERROR_INVALID_PARAMETER);
4348 memcpy( &mii, lpmii, lpmii->cbSize);
4349 if( lpmii->cbSize != sizeof( mii)) {
4350 mii.cbSize = sizeof( mii);
4351 mii.hbmpItem = NULL;
4353 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4354 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4357 /**********************************************************************
4358 * SetMenuDefaultItem (USER32.@)
4361 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4367 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4369 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4371 /* reset all default-item flags */
4373 for (i = 0; i < menu->nItems; i++, item++)
4375 item->fState &= ~MFS_DEFAULT;
4378 /* no default item */
4387 if ( uItem >= menu->nItems ) return FALSE;
4388 item[uItem].fState |= MFS_DEFAULT;
4393 for (i = 0; i < menu->nItems; i++, item++)
4395 if (item->wID == uItem)
4397 item->fState |= MFS_DEFAULT;
4406 /**********************************************************************
4407 * GetMenuDefaultItem (USER32.@)
4409 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4415 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4417 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4419 /* find default item */
4423 if (! item) return -1;
4425 while ( !( item->fState & MFS_DEFAULT ) )
4428 if (i >= menu->nItems ) return -1;
4431 /* default: don't return disabled items */
4432 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4434 /* search rekursiv when needed */
4435 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4438 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4439 if ( -1 != ret ) return ret;
4441 /* when item not found in submenu, return the popup item */
4443 return ( bypos ) ? i : item->wID;
4448 /**********************************************************************
4449 * InsertMenuItemA (USER32.@)
4451 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4452 const MENUITEMINFOA *lpmii)
4454 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4456 if( lpmii->cbSize != sizeof( mii) &&
4457 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4458 SetLastError( ERROR_INVALID_PARAMETER);
4461 memcpy( &mii, lpmii, lpmii->cbSize);
4462 if( lpmii->cbSize != sizeof( mii)) {
4463 mii.cbSize = sizeof( mii);
4464 mii.hbmpItem = NULL;
4466 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4470 /**********************************************************************
4471 * InsertMenuItemW (USER32.@)
4473 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4474 const MENUITEMINFOW *lpmii)
4476 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4478 if( lpmii->cbSize != sizeof( mii) &&
4479 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4480 SetLastError( ERROR_INVALID_PARAMETER);
4483 memcpy( &mii, lpmii, lpmii->cbSize);
4484 if( lpmii->cbSize != sizeof( mii)) {
4485 mii.cbSize = sizeof( mii);
4486 mii.hbmpItem = NULL;
4488 return SetMenuItemInfo_common(item, &mii, TRUE);
4491 /**********************************************************************
4492 * CheckMenuRadioItem (USER32.@)
4495 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4496 UINT first, UINT last, UINT check,
4499 MENUITEM *mifirst, *milast, *micheck;
4500 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4502 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4504 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4505 milast = MENU_FindItem (&mlast, &last, bypos);
4506 micheck = MENU_FindItem (&mcheck, &check, bypos);
4508 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4509 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4510 micheck > milast || micheck < mifirst)
4513 while (mifirst <= milast)
4515 if (mifirst == micheck)
4517 mifirst->fType |= MFT_RADIOCHECK;
4518 mifirst->fState |= MFS_CHECKED;
4520 mifirst->fType &= ~MFT_RADIOCHECK;
4521 mifirst->fState &= ~MFS_CHECKED;
4530 /**********************************************************************
4531 * GetMenuItemRect (USER32.@)
4533 * ATTENTION: Here, the returned values in rect are the screen
4534 * coordinates of the item just like if the menu was
4535 * always on the upper left side of the application.
4538 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4541 POPUPMENU *itemMenu;
4545 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4547 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4548 referenceHwnd = hwnd;
4552 itemMenu = MENU_GetMenu(hMenu);
4553 if (itemMenu == NULL)
4556 if(itemMenu->hWnd == 0)
4558 referenceHwnd = itemMenu->hWnd;
4561 if ((rect == NULL) || (item == NULL))
4566 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4572 /**********************************************************************
4573 * SetMenuInfo (USER32.@)
4576 * MIM_APPLYTOSUBMENUS
4577 * actually use the items to draw the menu
4579 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4583 TRACE("(%p %p)\n", hMenu, lpmi);
4585 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4588 if (lpmi->fMask & MIM_BACKGROUND)
4589 menu->hbrBack = lpmi->hbrBack;
4591 if (lpmi->fMask & MIM_HELPID)
4592 menu->dwContextHelpID = lpmi->dwContextHelpID;
4594 if (lpmi->fMask & MIM_MAXHEIGHT)
4595 menu->cyMax = lpmi->cyMax;
4597 if (lpmi->fMask & MIM_MENUDATA)
4598 menu->dwMenuData = lpmi->dwMenuData;
4600 if (lpmi->fMask & MIM_STYLE)
4602 menu->dwStyle = lpmi->dwStyle;
4603 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4604 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4605 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4606 if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4607 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4615 /**********************************************************************
4616 * GetMenuInfo (USER32.@)
4622 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4625 TRACE("(%p %p)\n", hMenu, lpmi);
4627 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4630 if (lpmi->fMask & MIM_BACKGROUND)
4631 lpmi->hbrBack = menu->hbrBack;
4633 if (lpmi->fMask & MIM_HELPID)
4634 lpmi->dwContextHelpID = menu->dwContextHelpID;
4636 if (lpmi->fMask & MIM_MAXHEIGHT)
4637 lpmi->cyMax = menu->cyMax;
4639 if (lpmi->fMask & MIM_MENUDATA)
4640 lpmi->dwMenuData = menu->dwMenuData;
4642 if (lpmi->fMask & MIM_STYLE)
4643 lpmi->dwStyle = menu->dwStyle;
4651 /**********************************************************************
4652 * SetMenuContextHelpId (USER32.@)
4654 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4658 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4660 if ((menu = MENU_GetMenu(hMenu)))
4662 menu->dwContextHelpID = dwContextHelpID;
4669 /**********************************************************************
4670 * GetMenuContextHelpId (USER32.@)
4672 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4676 TRACE("(%p)\n", hMenu);
4678 if ((menu = MENU_GetMenu(hMenu)))
4680 return menu->dwContextHelpID;
4685 /**********************************************************************
4686 * MenuItemFromPoint (USER32.@)
4688 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4690 POPUPMENU *menu = MENU_GetMenu(hMenu);
4693 /*FIXME: Do we have to handle hWnd here? */
4694 if (!menu) return -1;
4695 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4700 /**********************************************************************
4701 * translate_accelerator
4703 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4704 BYTE fVirt, WORD key, WORD cmd )
4709 if (wParam != key) return FALSE;
4711 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4712 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4713 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4715 if (message == WM_CHAR || message == WM_SYSCHAR)
4717 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4719 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4725 if(fVirt & FVIRTKEY)
4727 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4728 wParam, 0xff & HIWORD(lParam));
4730 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4731 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4735 if (!(lParam & 0x01000000)) /* no special_key */
4737 if ((fVirt & FALT) && (lParam & 0x20000000))
4738 { /* ^^ ALT pressed */
4739 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4748 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4752 HMENU hMenu, hSubMenu, hSysMenu;
4753 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4755 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4756 hSysMenu = get_win_sys_menu( hWnd );
4758 /* find menu item and ask application to initialize it */
4759 /* 1. in the system menu */
4760 hSubMenu = hSysMenu;
4762 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4766 if (!IsWindowEnabled(hWnd))
4770 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4771 if(hSubMenu != hSysMenu)
4773 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4774 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4775 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4777 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4780 else /* 2. in the window's menu */
4784 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4788 if (!IsWindowEnabled(hWnd))
4792 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4793 if(hSubMenu != hMenu)
4795 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4796 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4797 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4799 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4806 if (uSysStat != (UINT)-1)
4808 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4815 if (uStat != (UINT)-1)
4821 if (uStat & (MF_DISABLED|MF_GRAYED))
4833 if( mesg==WM_COMMAND )
4835 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4836 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4838 else if( mesg==WM_SYSCOMMAND )
4840 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4841 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4845 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4846 * #0: unknown (please report!)
4847 * #1: for WM_KEYUP,WM_SYSKEYUP
4848 * #2: mouse is captured
4849 * #3: window is disabled
4850 * #4: it's a disabled system menu option
4851 * #5: it's a menu option, but window is iconic
4852 * #6: it's a menu option, but disabled
4854 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4856 ERR_(accel)(" unknown reason - please report!\n");
4861 /**********************************************************************
4862 * TranslateAcceleratorA (USER32.@)
4863 * TranslateAccelerator (USER32.@)
4865 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4868 LPACCEL16 lpAccelTbl;
4872 if (!hWnd || !msg) return 0;
4874 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4876 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4880 wParam = msg->wParam;
4882 switch (msg->message)
4891 char ch = LOWORD(wParam);
4893 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4894 wParam = MAKEWPARAM(wch, HIWORD(wParam));
4902 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4903 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4907 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4908 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4910 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4915 /**********************************************************************
4916 * TranslateAcceleratorW (USER32.@)
4918 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4921 LPACCEL16 lpAccelTbl;
4924 if (!hWnd || !msg) return 0;
4926 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4928 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4932 switch (msg->message)
4944 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4945 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4949 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4950 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4952 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);