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 or bitmap handle. */
76 DWORD dwItemData; /* Application defined. */
77 DWORD dwTypeData; /* depends on fMask */
78 HBITMAP hbmpItem; /* bitmap in win98 style menus */
79 /* ----------- Wine stuff ----------- */
80 RECT rect; /* Item area (relative to menu window) */
81 UINT xTab; /* X position of text after Tab */
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 /* popup menu shade thickness */
133 #define POPUP_XSHADE 4
134 #define POPUP_YSHADE 4
136 /* Space between 2 menu bar items */
137 #define MENU_BAR_ITEMS_SPACE 12
139 /* Minimum width of a tab character */
140 #define MENU_TAB_SPACE 8
142 /* Height of a separator item */
143 #define SEPARATOR_HEIGHT 5
145 /* Space between 2 columns */
146 #define MENU_COL_SPACE 4
148 /* (other menu->FocusedItem values give the position of the focused item) */
149 #define NO_SELECTED_ITEM 0xffff
151 #define MENU_ITEM_TYPE(flags) \
152 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
154 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
155 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
156 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
158 #define IS_SYSTEM_MENU(menu) \
159 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
161 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
162 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
163 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
164 MF_POPUP | MF_SYSMENU | MF_HELP)
165 #define STATE_MASK (~TYPE_MASK)
167 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
169 /* Dimension of the menu bitmaps */
170 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
172 static HBITMAP hStdMnArrow = 0;
173 static HBITMAP hBmpSysMenu = 0;
175 static HBRUSH hShadeBrush = 0;
176 static HFONT hMenuFont = 0;
177 static HFONT hMenuFontBold = 0;
179 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
181 /* Use global popup window because there's no way 2 menus can
182 * be tracked at the same time. */
183 static HWND top_popup;
185 /* Flag set by EndMenu() to force an exit from menu tracking */
186 static BOOL fEndMenu = FALSE;
188 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
190 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
192 /*********************************************************************
193 * menu class descriptor
195 const struct builtin_class_descr MENU_builtin_class =
197 POPUPMENU_CLASS_ATOMA, /* name */
198 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
199 NULL, /* procA (winproc is Unicode only) */
200 PopupMenuWndProc, /* procW */
201 sizeof(HMENU), /* extra */
202 IDC_ARROW, /* cursor */
203 (HBRUSH)(COLOR_MENU+1) /* brush */
207 /***********************************************************************
208 * debug_print_menuitem
210 * Print a menuitem in readable form.
213 #define debug_print_menuitem(pre, mp, post) \
214 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
216 #define MENUOUT(text) \
217 DPRINTF("%s%s", (count++ ? "," : ""), (text))
219 #define MENUFLAG(bit,text) \
221 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
224 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
227 TRACE("%s ", prefix);
229 UINT flags = mp->fType;
230 int type = MENU_ITEM_TYPE(flags);
231 DPRINTF( "{ ID=0x%x", mp->wID);
232 if (flags & MF_POPUP)
233 DPRINTF( ", Sub=%p", mp->hSubMenu);
237 if (type == MFT_STRING)
239 else if (type == MFT_SEPARATOR)
241 else if (type == MFT_OWNERDRAW)
243 else if (type == MFT_BITMAP)
249 MENUFLAG(MF_POPUP, "pop");
250 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
251 MENUFLAG(MFT_MENUBREAK, "brk");
252 MENUFLAG(MFT_RADIOCHECK, "radio");
253 MENUFLAG(MFT_RIGHTORDER, "rorder");
254 MENUFLAG(MF_SYSMENU, "sys");
255 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
258 DPRINTF( "+0x%x", flags);
263 DPRINTF( ", State=");
264 MENUFLAG(MFS_GRAYED, "grey");
265 MENUFLAG(MFS_DEFAULT, "default");
266 MENUFLAG(MFS_DISABLED, "dis");
267 MENUFLAG(MFS_CHECKED, "check");
268 MENUFLAG(MFS_HILITE, "hi");
269 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
270 MENUFLAG(MF_MOUSESELECT, "mouse");
272 DPRINTF( "+0x%x", flags);
275 DPRINTF( ", Chk=%p", mp->hCheckBit);
277 DPRINTF( ", Unc=%p", mp->hUnCheckBit);
279 if (type == MFT_STRING) {
281 DPRINTF( ", Text=%s", debugstr_w(mp->text));
283 DPRINTF( ", Text=Null");
284 } else if (mp->text == NULL)
287 DPRINTF( ", Text=%p", mp->text);
289 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
295 DPRINTF(" %s\n", postfix);
302 /***********************************************************************
305 * Validate the given menu handle and returns the menu structure pointer.
307 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
309 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
310 if (!menu || menu->wMagic != MENU_MAGIC)
312 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
318 /***********************************************************************
321 * Get the system menu of a window
323 static HMENU get_win_sys_menu( HWND hwnd )
326 WND *win = WIN_GetPtr( hwnd );
327 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
330 WIN_ReleasePtr( win );
335 /***********************************************************************
338 * Return the default system menu.
340 static HMENU MENU_CopySysPopup(void)
342 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
343 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
346 POPUPMENU* menu = MENU_GetMenu(hMenu);
347 menu->wFlags |= MF_SYSMENU | MF_POPUP;
348 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
351 ERR("Unable to load default system menu\n" );
353 TRACE("returning %p.\n", hMenu );
359 /**********************************************************************
362 * Create a copy of the system menu. System menu in Windows is
363 * a special menu bar with the single entry - system menu popup.
364 * This popup is presented to the outside world as a "system menu".
365 * However, the real system menu handle is sometimes seen in the
366 * WM_MENUSELECT parameters (and Word 6 likes it this way).
368 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
372 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
373 if ((hMenu = CreateMenu()))
375 POPUPMENU *menu = MENU_GetMenu(hMenu);
376 menu->wFlags = MF_SYSMENU;
377 menu->hWnd = WIN_GetFullHandle( hWnd );
378 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
380 if (hPopupMenu == (HMENU)(-1))
381 hPopupMenu = MENU_CopySysPopup();
382 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
386 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
387 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
389 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
390 (UINT_PTR)hPopupMenu, NULL );
392 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
393 menu->items[0].fState = 0;
394 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
396 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
399 DestroyMenu( hMenu );
401 ERR("failed to load system menu!\n");
406 /***********************************************************************
409 * Menus initialisation.
414 NONCLIENTMETRICSW ncm;
416 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
421 /* Load menu bitmaps */
422 hStdMnArrow = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
423 /* Load system buttons bitmaps */
424 hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
429 GetObjectW( hStdMnArrow, sizeof(bm), &bm );
430 arrow_bitmap_width = bm.bmWidth;
431 arrow_bitmap_height = bm.bmHeight;
435 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
438 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
441 DeleteObject( hBitmap );
442 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
445 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
446 if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0)))
449 if (!(hMenuFont = CreateFontIndirectW( &ncm.lfMenuFont )))
452 ncm.lfMenuFont.lfWeight += 300;
453 if ( ncm.lfMenuFont.lfWeight > 1000)
454 ncm.lfMenuFont.lfWeight = 1000;
456 if (!(hMenuFontBold = CreateFontIndirectW( &ncm.lfMenuFont )))
462 /***********************************************************************
463 * MENU_InitSysMenuPopup
465 * Grey the appropriate items in System menu.
467 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
471 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
472 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
473 gray = ((style & WS_MAXIMIZE) != 0);
474 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
475 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
476 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
477 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
478 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
479 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
480 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
481 gray = (clsStyle & CS_NOCLOSE) != 0;
483 /* The menu item must keep its state if it's disabled */
485 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
489 /******************************************************************************
491 * UINT MENU_GetStartOfNextColumn(
494 *****************************************************************************/
496 static UINT MENU_GetStartOfNextColumn(
499 POPUPMENU *menu = MENU_GetMenu(hMenu);
503 return NO_SELECTED_ITEM;
505 i = menu->FocusedItem + 1;
506 if( i == NO_SELECTED_ITEM )
509 for( ; i < menu->nItems; ++i ) {
510 if (menu->items[i].fType & MF_MENUBARBREAK)
514 return NO_SELECTED_ITEM;
518 /******************************************************************************
520 * UINT MENU_GetStartOfPrevColumn(
523 *****************************************************************************/
525 static UINT MENU_GetStartOfPrevColumn(
528 POPUPMENU *menu = MENU_GetMenu(hMenu);
532 return NO_SELECTED_ITEM;
534 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
535 return NO_SELECTED_ITEM;
537 /* Find the start of the column */
539 for(i = menu->FocusedItem; i != 0 &&
540 !(menu->items[i].fType & MF_MENUBARBREAK);
544 return NO_SELECTED_ITEM;
546 for(--i; i != 0; --i) {
547 if (menu->items[i].fType & MF_MENUBARBREAK)
551 TRACE("ret %d.\n", i );
558 /***********************************************************************
561 * Find a menu item. Return a pointer on the item, and modifies *hmenu
562 * in case the item was in a sub-menu.
564 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
569 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
570 if (wFlags & MF_BYPOSITION)
572 if (*nPos >= menu->nItems) return NULL;
573 return &menu->items[*nPos];
577 MENUITEM *item = menu->items;
578 for (i = 0; i < menu->nItems; i++, item++)
580 if (item->wID == *nPos)
585 else if (item->fType & MF_POPUP)
587 HMENU hsubmenu = item->hSubMenu;
588 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
600 /***********************************************************************
603 * Find a Sub menu. Return the position of the submenu, and modifies
604 * *hmenu in case it is found in another sub-menu.
605 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
607 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
612 if (((*hmenu)==(HMENU)0xffff) ||
613 (!(menu = MENU_GetMenu(*hmenu))))
614 return NO_SELECTED_ITEM;
616 for (i = 0; i < menu->nItems; i++, item++) {
617 if(!(item->fType & MF_POPUP)) continue;
618 if (item->hSubMenu == hSubTarget) {
622 HMENU hsubmenu = item->hSubMenu;
623 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
624 if (pos != NO_SELECTED_ITEM) {
630 return NO_SELECTED_ITEM;
633 /***********************************************************************
636 static void MENU_FreeItemData( MENUITEM* item )
639 if (IS_STRING_ITEM(item->fType) && item->text)
640 HeapFree( GetProcessHeap(), 0, item->text );
643 /***********************************************************************
644 * MENU_FindItemByCoords
646 * Find the item at the specified coordinates (screen coords). Does
647 * not work for child windows and therefore should not be called for
648 * an arbitrary system menu.
650 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
651 POINT pt, UINT *pos )
657 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
658 pt.x -= wrect.left;pt.y -= wrect.top;
660 for (i = 0; i < menu->nItems; i++, item++)
662 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
663 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
673 /***********************************************************************
676 * Find the menu item selected by a key press.
677 * Return item id, -1 if none, -2 if we should close the menu.
679 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
680 WCHAR key, BOOL forceMenuChar )
682 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
684 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
688 POPUPMENU *menu = MENU_GetMenu( hmenu );
689 MENUITEM *item = menu->items;
696 for (i = 0; i < menu->nItems; i++, item++)
698 if (IS_STRING_ITEM(item->fType) && item->text)
700 WCHAR *p = item->text - 2;
703 p = strchrW (p + 2, '&');
705 while (p != NULL && p [1] == '&');
706 if (p && (toupperW(p[1]) == toupperW(key))) return i;
710 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
711 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
712 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
713 if (HIWORD(menuchar) == 1) return (UINT)(-2);
719 /***********************************************************************
720 * MENU_GetBitmapItemSize
722 * Get the size of a bitmap item.
724 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
727 HBITMAP bmp = (HBITMAP)id;
729 size->cx = size->cy = 0;
731 /* check if there is a magic menu item associated with this item */
732 if (id && IS_MAGIC_ITEM( id ))
736 case (INT_PTR)HBMMENU_SYSTEM:
743 case (INT_PTR)HBMMENU_MBAR_RESTORE:
744 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
745 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
746 case (INT_PTR)HBMMENU_MBAR_CLOSE:
747 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
748 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
751 case (INT_PTR)HBMMENU_CALLBACK:
752 case (INT_PTR)HBMMENU_POPUP_CLOSE:
753 case (INT_PTR)HBMMENU_POPUP_RESTORE:
754 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
755 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
757 FIXME("Magic 0x%08x not implemented\n", id);
761 if (GetObjectW(bmp, sizeof(bm), &bm ))
763 size->cx = bm.bmWidth;
764 size->cy = bm.bmHeight;
768 /***********************************************************************
769 * MENU_DrawBitmapItem
771 * Draw a bitmap item.
772 * drawhbmbitmap : True to draw the hbmbitmap(MIIM_BITMAP)/False to draw the MF_BITMAP
774 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar, BOOL drawhbmbitmap )
779 HBITMAP bmp = (HBITMAP)lpitem->text;
780 int w = rect->right - rect->left;
781 int h = rect->bottom - rect->top;
784 HBITMAP hbmToDraw = (drawhbmbitmap)?lpitem->hbmpItem:(HBITMAP)lpitem->text;
786 /* Check if there is a magic menu item associated with this item */
787 if (hbmToDraw && IS_MAGIC_ITEM(hbmToDraw))
792 switch(LOWORD(hbmToDraw))
794 case (INT_PTR)HBMMENU_SYSTEM:
795 if (lpitem->dwItemData)
797 bmp = (HBITMAP)lpitem->dwItemData;
798 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
803 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
804 /* only use right half of the bitmap */
805 bmp_xoffset = bm.bmWidth / 2;
806 bm.bmWidth -= bmp_xoffset;
809 case (INT_PTR)HBMMENU_MBAR_RESTORE:
810 flags = DFCS_CAPTIONRESTORE;
812 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
813 flags = DFCS_CAPTIONMIN;
815 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
816 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
818 case (INT_PTR)HBMMENU_MBAR_CLOSE:
819 flags = DFCS_CAPTIONCLOSE;
821 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
822 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
824 case (INT_PTR)HBMMENU_CALLBACK:
825 case (INT_PTR)HBMMENU_POPUP_CLOSE:
826 case (INT_PTR)HBMMENU_POPUP_RESTORE:
827 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
828 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
830 FIXME("Magic 0x%08x not implemented\n", LOWORD(hbmToDraw));
834 InflateRect( &r, -1, -1 );
835 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
836 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
840 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
843 hdcMem = CreateCompatibleDC( hdc );
844 SelectObject( hdcMem, bmp );
846 /* handle fontsize > bitmap_height */
847 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
849 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
850 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
851 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
852 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
857 /***********************************************************************
860 * Calculate the size of the menu item and store it in lpitem->rect.
862 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
863 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
866 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
868 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
869 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
870 (menuBar ? " (MenuBar)" : ""));
872 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
874 if (lpitem->fType & MF_OWNERDRAW)
877 ** Experimentation under Windows reveals that an owner-drawn
878 ** menu is expected to return the size of the content part of
879 ** the menu item, not including the checkmark nor the submenu
880 ** arrow. Windows adds those values itself and returns the
881 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
883 MEASUREITEMSTRUCT mis;
884 mis.CtlType = ODT_MENU;
886 mis.itemID = lpitem->wID;
887 mis.itemData = (DWORD)lpitem->dwItemData;
890 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
891 lpitem->rect.right += mis.itemWidth;
895 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
898 /* under at least win95 you seem to be given a standard
899 height for the menu and the height value is ignored */
900 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
903 lpitem->rect.bottom += mis.itemHeight;
905 TRACE("id=%04x size=%dx%d\n",
906 lpitem->wID, mis.itemWidth, mis.itemHeight);
907 /* Fall through to get check/arrow width calculation. */
910 if (lpitem->fType & MF_SEPARATOR)
912 lpitem->rect.bottom += SEPARATOR_HEIGHT;
918 /* New style MIIM_BITMAP */
919 if (lpitem->hbmpItem)
921 if (lpitem->hbmpItem == HBMMENU_CALLBACK)
923 MEASUREITEMSTRUCT measItem;
924 measItem.CtlType = ODT_MENU;
926 measItem.itemID = lpitem->wID;
927 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
928 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
929 measItem.itemData = lpitem->dwItemData;
931 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
933 /* Keep the size of the bitmap in callback mode to be able to draw it correctly */
934 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, measItem.itemWidth - (lpitem->rect.right - lpitem->rect.left));
935 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, measItem.itemHeight - (lpitem->rect.bottom - lpitem->rect.top));
936 lpitem->rect.right = lpitem->rect.left + measItem.itemWidth;
939 MENU_GetBitmapItemSize((UINT)lpitem->hbmpItem, lpitem->dwItemData, &size);
940 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, size.cx);
941 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, size.cy);
942 lpitem->rect.right += size.cx;
943 lpitem->rect.bottom += size.cy;
945 if (lppop->dwStyle & MNS_CHECKORBMP)
946 lpitem->rect.right += check_bitmap_width;
948 lpitem->rect.right += 2 * check_bitmap_width;
950 lpitem->rect.right += 2 * check_bitmap_width;
951 if (lpitem->fType & MF_POPUP)
952 lpitem->rect.right += arrow_bitmap_width;
955 if (lpitem->fType & MF_OWNERDRAW)
958 if (IS_BITMAP_ITEM(lpitem->fType))
962 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
963 lpitem->rect.right += size.cx;
964 lpitem->rect.bottom += size.cy;
965 /* Leave space for the sunken border */
966 lpitem->rect.right += 2;
967 lpitem->rect.bottom += 2;
971 /* it must be a text item - unless it's the system menu */
972 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
975 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
977 lpitem->rect.right += size.cx;
978 lpitem->rect.bottom += max(max(size.cy, GetSystemMetrics(SM_CYMENU)-1), lppop->maxBmpSize.cy);
983 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
985 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
987 /* Item contains a tab (only meaningful in popup menus) */
988 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
989 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
990 lpitem->rect.right += MENU_TAB_SPACE;
994 if (strchrW( lpitem->text, '\b' ))
995 lpitem->rect.right += MENU_TAB_SPACE;
996 lpitem->xTab = lpitem->rect.right - check_bitmap_width
997 - arrow_bitmap_width;
1000 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
1004 /***********************************************************************
1005 * MENU_PopupMenuCalcSize
1007 * Calculate the size of a popup menu.
1009 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1014 int orgX, orgY, maxX, maxTab, maxTabWidth;
1016 lppop->Width = lppop->Height = 0;
1017 if (lppop->nItems == 0) return;
1020 SelectObject( hdc, hMenuFont);
1025 lppop->maxBmpSize.cx = 0;
1026 lppop->maxBmpSize.cy = 0;
1028 while (start < lppop->nItems)
1030 lpitem = &lppop->items[start];
1032 if( lpitem->fType & MF_MENUBREAK)
1033 orgX += MENU_COL_SPACE;
1036 maxTab = maxTabWidth = 0;
1037 /* Parse items until column break or end of menu */
1038 for (i = start; i < lppop->nItems; i++, lpitem++)
1041 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1043 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1045 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1046 maxX = max( maxX, lpitem->rect.right );
1047 orgY = lpitem->rect.bottom;
1048 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1050 maxTab = max( maxTab, lpitem->xTab );
1051 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1055 /* Finish the column (set all items to the largest width found) */
1056 maxX = max( maxX, maxTab + maxTabWidth );
1057 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1059 lpitem->rect.right = maxX;
1060 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1061 lpitem->xTab = maxTab;
1064 lppop->Height = max( lppop->Height, orgY );
1067 lppop->Width = maxX;
1069 /* space for 3d border */
1073 ReleaseDC( 0, hdc );
1077 /***********************************************************************
1078 * MENU_MenuBarCalcSize
1080 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1081 * height is off by 1 pixel which causes lengthy window relocations when
1082 * active document window is maximized/restored.
1084 * Calculate the size of the menu bar.
1086 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1087 LPPOPUPMENU lppop, HWND hwndOwner )
1090 int start, i, orgX, orgY, maxY, helpPos;
1092 if ((lprect == NULL) || (lppop == NULL)) return;
1093 if (lppop->nItems == 0) return;
1094 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1095 lprect->left, lprect->top, lprect->right, lprect->bottom);
1096 lppop->Width = lprect->right - lprect->left;
1098 maxY = lprect->top+1;
1101 lppop->maxBmpSize.cx = 0;
1102 lppop->maxBmpSize.cy = 0;
1103 while (start < lppop->nItems)
1105 lpitem = &lppop->items[start];
1106 orgX = lprect->left;
1109 /* Parse items until line break or end of menu */
1110 for (i = start; i < lppop->nItems; i++, lpitem++)
1112 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1114 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1116 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1118 debug_print_menuitem (" item: ", lpitem, "");
1119 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1121 if (lpitem->rect.right > lprect->right)
1123 if (i != start) break;
1124 else lpitem->rect.right = lprect->right;
1126 maxY = max( maxY, lpitem->rect.bottom );
1127 orgX = lpitem->rect.right;
1130 /* Finish the line (set all items to the largest height found) */
1131 while (start < i) lppop->items[start++].rect.bottom = maxY;
1134 lprect->bottom = maxY;
1135 lppop->Height = lprect->bottom - lprect->top;
1137 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1138 /* the last item (if several lines, only move the last line) */
1139 lpitem = &lppop->items[lppop->nItems-1];
1140 orgY = lpitem->rect.top;
1141 orgX = lprect->right;
1142 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1143 if ( (helpPos==-1) || (helpPos>i) )
1145 if (lpitem->rect.top != orgY) break; /* Other line */
1146 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1147 lpitem->rect.left += orgX - lpitem->rect.right;
1148 lpitem->rect.right = orgX;
1149 orgX = lpitem->rect.left;
1153 /***********************************************************************
1156 * Draw a single menu item.
1158 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1159 UINT height, BOOL menuBar, UINT odaction )
1163 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1165 if (lpitem->fType & MF_SYSMENU)
1167 if( !IsIconic(hwnd) )
1168 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1174 if (lpitem->fState & MF_HILITE)
1177 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1178 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1180 if(lpitem->fState & MF_GRAYED)
1181 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1183 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1184 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1189 if (lpitem->fState & MF_GRAYED)
1190 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1192 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1193 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1196 if (lpitem->fType & MF_OWNERDRAW)
1199 ** Experimentation under Windows reveals that an owner-drawn
1200 ** menu is given the rectangle which includes the space it requested
1201 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1202 ** and a popup-menu arrow. This is the value of lpitem->rect.
1203 ** Windows will leave all drawing to the application except for
1204 ** the popup-menu arrow. Windows always draws that itself, after
1205 ** the menu owner has finished drawing.
1209 dis.CtlType = ODT_MENU;
1211 dis.itemID = lpitem->wID;
1212 dis.itemData = (DWORD)lpitem->dwItemData;
1214 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1215 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1216 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1217 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1218 dis.hwndItem = (HWND)hmenu;
1220 dis.rcItem = lpitem->rect;
1221 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1222 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1223 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1224 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1226 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1227 /* Fall through to draw popup-menu arrow */
1230 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1231 lpitem->rect.right,lpitem->rect.bottom);
1233 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1235 rect = lpitem->rect;
1237 if (!(lpitem->fType & MF_OWNERDRAW))
1239 if (lpitem->fState & MF_HILITE)
1242 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1244 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1247 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1250 SetBkMode( hdc, TRANSPARENT );
1252 if (!(lpitem->fType & MF_OWNERDRAW))
1254 /* vertical separator */
1255 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1259 rc.bottom = height - 3;
1260 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1263 /* horizontal separator */
1264 if (lpitem->fType & MF_SEPARATOR)
1269 rc.top += SEPARATOR_HEIGHT / 2;
1270 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1275 /* helper lines for debugging */
1276 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1277 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1278 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1279 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1285 INT y = rect.top + rect.bottom;
1286 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1287 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1289 if (!(lpitem->fType & MF_OWNERDRAW))
1291 /* New style MIIM_BITMAP */
1292 if (lpitem->hbmpItem)
1294 POPUPMENU *menu = MENU_GetMenu(hmenu);
1295 HBITMAP hbm = lpitem->hbmpItem;
1297 if (hbm == HBMMENU_CALLBACK)
1299 DRAWITEMSTRUCT drawItem;
1300 drawItem.CtlType = ODT_MENU;
1302 drawItem.itemID = lpitem->wID;
1303 drawItem.itemAction = odaction;
1304 drawItem.itemState |= (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1305 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1306 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1307 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1308 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1309 drawItem.hwndItem = (HWND)hmenu;
1311 drawItem.rcItem = lpitem->rect;
1312 drawItem.itemData = lpitem->dwItemData;
1314 if (!(lpitem->fState & MF_CHECKED))
1315 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1318 MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE, TRUE);
1320 if (menu->dwStyle & MNS_CHECKORBMP)
1321 rect.left += menu->maxBmpSize.cx - check_bitmap_width;
1323 rect.left += menu->maxBmpSize.cx;
1325 /* Draw the check mark
1328 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1330 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1331 if (bm) /* we have a custom bitmap */
1333 HDC hdcMem = CreateCompatibleDC( hdc );
1334 SelectObject( hdcMem, bm );
1335 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1336 check_bitmap_width, check_bitmap_height,
1337 hdcMem, 0, 0, SRCCOPY );
1340 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1343 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1344 HDC hdcMem = CreateCompatibleDC( hdc );
1345 SelectObject( hdcMem, bm );
1346 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1347 DrawFrameControl( hdcMem, &r, DFC_MENU,
1348 (lpitem->fType & MFT_RADIOCHECK) ?
1349 DFCS_MENUBULLET : DFCS_MENUCHECK );
1350 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1351 hdcMem, 0, 0, SRCCOPY );
1357 /* Draw the popup-menu arrow */
1358 if (lpitem->fType & MF_POPUP)
1360 HDC hdcMem = CreateCompatibleDC( hdc );
1361 HBITMAP hOrigBitmap;
1363 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1364 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1365 (y - arrow_bitmap_height) / 2,
1366 arrow_bitmap_width, arrow_bitmap_height,
1367 hdcMem, 0, 0, SRCCOPY );
1368 SelectObject( hdcMem, hOrigBitmap );
1372 rect.left += check_bitmap_width;
1373 rect.right -= arrow_bitmap_width;
1376 /* Done for owner-drawn */
1377 if (lpitem->fType & MF_OWNERDRAW)
1380 /* Draw the item text or bitmap */
1381 if (IS_BITMAP_ITEM(lpitem->fType))
1383 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar, FALSE);
1386 /* No bitmap - process text if present */
1387 else if (IS_STRING_ITEM(lpitem->fType))
1392 UINT uFormat = (menuBar) ?
1393 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1394 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1396 if ( lpitem->fState & MFS_DEFAULT )
1398 hfontOld = SelectObject( hdc, hMenuFontBold);
1403 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1404 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1407 for (i = 0; lpitem->text[i]; i++)
1408 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1411 if(lpitem->fState & MF_GRAYED)
1413 if (!(lpitem->fState & MF_HILITE) )
1415 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1416 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1417 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1418 --rect.left; --rect.top; --rect.right; --rect.bottom;
1420 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1423 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1425 /* paint the shortcut text */
1426 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1428 if (lpitem->text[i] == '\t')
1430 rect.left = lpitem->xTab;
1431 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1435 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1438 if(lpitem->fState & MF_GRAYED)
1440 if (!(lpitem->fState & MF_HILITE) )
1442 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1443 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1444 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1445 --rect.left; --rect.top; --rect.right; --rect.bottom;
1447 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1449 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1453 SelectObject (hdc, hfontOld);
1458 /***********************************************************************
1459 * MENU_DrawPopupMenu
1461 * Paint a popup menu.
1463 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1465 HBRUSH hPrevBrush = 0;
1468 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1470 GetClientRect( hwnd, &rect );
1472 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1473 && (SelectObject( hdc, hMenuFont)))
1477 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1479 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1484 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1486 /* draw menu items */
1488 menu = MENU_GetMenu( hmenu );
1489 if (menu && menu->nItems)
1494 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1495 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1496 menu->Height, FALSE, ODA_DRAWENTIRE );
1501 SelectObject( hdc, hPrevBrush );
1506 /***********************************************************************
1509 * Paint a menu bar. Returns the height of the menu bar.
1510 * called from [windows/nonclient.c]
1512 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1517 HMENU hMenu = GetMenu(hwnd);
1519 lppop = MENU_GetMenu( hMenu );
1520 if (lppop == NULL || lprect == NULL)
1522 return GetSystemMetrics(SM_CYMENU);
1527 hfontOld = SelectObject( hDC, hMenuFont);
1529 if (lppop->Height == 0)
1530 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1532 lprect->bottom = lprect->top + lppop->Height;
1534 if (hfontOld) SelectObject( hDC, hfontOld);
1535 return lppop->Height;
1538 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1542 /***********************************************************************
1545 * Display a popup menu.
1547 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1548 INT x, INT y, INT xanchor, INT yanchor )
1553 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1554 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1556 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1557 if (menu->FocusedItem != NO_SELECTED_ITEM)
1559 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1560 menu->FocusedItem = NO_SELECTED_ITEM;
1563 /* store the owner for DrawItem */
1564 menu->hwndOwner = hwndOwner;
1566 MENU_PopupMenuCalcSize( menu, hwndOwner );
1568 /* adjust popup menu pos so that it fits within the desktop */
1570 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1571 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1573 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1576 x -= width - xanchor;
1577 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1578 x = GetSystemMetrics(SM_CXSCREEN) - width;
1582 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1585 y -= height + yanchor;
1586 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1587 y = GetSystemMetrics(SM_CYSCREEN) - height;
1591 /* NOTE: In Windows, top menu popup is not owned. */
1592 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1593 WS_POPUP, x, y, width, height,
1594 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1596 if( !menu->hWnd ) return FALSE;
1597 if (!top_popup) top_popup = menu->hWnd;
1599 /* Display the window */
1601 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1602 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1603 UpdateWindow( menu->hWnd );
1608 /***********************************************************************
1611 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1612 BOOL sendMenuSelect, HMENU topmenu )
1617 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1619 lppop = MENU_GetMenu( hmenu );
1620 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1622 if (lppop->FocusedItem == wIndex) return;
1623 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1624 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1625 if (!top_popup) top_popup = lppop->hWnd;
1627 SelectObject( hdc, hMenuFont);
1629 /* Clear previous highlighted item */
1630 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1632 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1633 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1634 lppop->Height, !(lppop->wFlags & MF_POPUP),
1638 /* Highlight new item (if any) */
1639 lppop->FocusedItem = wIndex;
1640 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1642 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1643 lppop->items[wIndex].fState |= MF_HILITE;
1644 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1645 &lppop->items[wIndex], lppop->Height,
1646 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1650 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1651 SendMessageW( hwndOwner, WM_MENUSELECT,
1652 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1653 ip->fType | ip->fState |
1654 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1657 else if (sendMenuSelect) {
1660 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1661 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1662 MENUITEM *ip = &ptm->items[pos];
1663 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1664 ip->fType | ip->fState |
1665 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1669 ReleaseDC( lppop->hWnd, hdc );
1673 /***********************************************************************
1674 * MENU_MoveSelection
1676 * Moves currently selected item according to the offset parameter.
1677 * If there is no selection then it should select the last item if
1678 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1680 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1685 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1687 menu = MENU_GetMenu( hmenu );
1688 if ((!menu) || (!menu->items)) return;
1690 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1692 if( menu->nItems == 1 ) return; else
1693 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1695 if (!(menu->items[i].fType & MF_SEPARATOR))
1697 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1702 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1703 i >= 0 && i < menu->nItems ; i += offset)
1704 if (!(menu->items[i].fType & MF_SEPARATOR))
1706 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1712 /**********************************************************************
1715 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1718 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1721 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1723 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1724 TRACE("flags=%x str=%p\n", flags, str);
1726 if (IS_STRING_ITEM(flags))
1730 flags |= MF_SEPARATOR;
1736 /* Item beginning with a backspace is a help item */
1742 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1744 strcpyW( text, str );
1748 else if (IS_BITMAP_ITEM(flags))
1749 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1750 else item->text = NULL;
1752 if (flags & MF_OWNERDRAW)
1753 item->dwItemData = (DWORD)str;
1755 item->dwItemData = 0;
1757 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1758 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1760 if (flags & MF_POPUP)
1762 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1763 if (menu) menu->wFlags |= MF_POPUP;
1775 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1777 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1778 flags |= MF_POPUP; /* keep popup */
1780 item->fType = flags & TYPE_MASK;
1781 item->fState = (flags & STATE_MASK) &
1782 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1785 /* Don't call SetRectEmpty here! */
1788 HeapFree( GetProcessHeap(), 0, prevText );
1790 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1795 /**********************************************************************
1798 * Insert (allocate) a new item into a menu.
1800 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1805 if (!(menu = MENU_GetMenu(hMenu)))
1808 /* Find where to insert new item */
1810 if (flags & MF_BYPOSITION) {
1811 if (pos > menu->nItems)
1814 if (!MENU_FindItem( &hMenu, &pos, flags ))
1817 if (!(menu = MENU_GetMenu( hMenu )))
1822 /* Create new items array */
1824 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1827 WARN("allocation failed\n" );
1830 if (menu->nItems > 0)
1832 /* Copy the old array into the new one */
1833 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1834 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1835 (menu->nItems-pos)*sizeof(MENUITEM) );
1836 HeapFree( GetProcessHeap(), 0, menu->items );
1838 menu->items = newItems;
1840 memset( &newItems[pos], 0, sizeof(*newItems) );
1841 menu->Height = 0; /* force size recalculate */
1842 return &newItems[pos];
1846 /**********************************************************************
1847 * MENU_ParseResource
1849 * Parse a standard menu resource and add items to the menu.
1850 * Return a pointer to the end of the resource.
1852 * NOTE: flags is equivalent to the mtOption field
1854 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1861 flags = GET_WORD(res);
1862 res += sizeof(WORD);
1863 if (!(flags & MF_POPUP))
1866 res += sizeof(WORD);
1869 if (!unicode) res += strlen(str) + 1;
1870 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1871 if (flags & MF_POPUP)
1873 HMENU hSubMenu = CreatePopupMenu();
1874 if (!hSubMenu) return NULL;
1875 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1877 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1878 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1880 else /* Not a popup */
1882 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1883 else AppendMenuW( hMenu, flags, id,
1884 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1886 } while (!(flags & MF_END));
1891 /**********************************************************************
1892 * MENUEX_ParseResource
1894 * Parse an extended menu resource and add items to the menu.
1895 * Return a pointer to the end of the resource.
1897 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1903 mii.cbSize = sizeof(mii);
1904 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1905 mii.fType = GET_DWORD(res);
1906 res += sizeof(DWORD);
1907 mii.fState = GET_DWORD(res);
1908 res += sizeof(DWORD);
1909 mii.wID = GET_DWORD(res);
1910 res += sizeof(DWORD);
1911 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1912 res += sizeof(WORD);
1913 /* Align the text on a word boundary. */
1914 res += (~((int)res - 1)) & 1;
1915 mii.dwTypeData = (LPWSTR) res;
1916 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1917 /* Align the following fields on a dword boundary. */
1918 res += (~((int)res - 1)) & 3;
1920 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1921 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1923 if (resinfo & 1) { /* Pop-up? */
1924 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1925 res += sizeof(DWORD);
1926 mii.hSubMenu = CreatePopupMenu();
1929 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1930 DestroyMenu(mii.hSubMenu);
1933 mii.fMask |= MIIM_SUBMENU;
1934 mii.fType |= MF_POPUP;
1936 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1938 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1939 mii.wID, mii.fType);
1940 mii.fType |= MF_SEPARATOR;
1942 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1943 } while (!(resinfo & MF_END));
1948 /***********************************************************************
1951 * Return the handle of the selected sub-popup menu (if any).
1953 static HMENU MENU_GetSubPopup( HMENU hmenu )
1958 menu = MENU_GetMenu( hmenu );
1960 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1962 item = &menu->items[menu->FocusedItem];
1963 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1964 return item->hSubMenu;
1969 /***********************************************************************
1970 * MENU_HideSubPopups
1972 * Hide the sub-popup menus of this menu.
1974 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1975 BOOL sendMenuSelect )
1977 POPUPMENU *menu = MENU_GetMenu( hmenu );
1979 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1981 if (menu && top_popup)
1987 if (menu->FocusedItem != NO_SELECTED_ITEM)
1989 item = &menu->items[menu->FocusedItem];
1990 if (!(item->fType & MF_POPUP) ||
1991 !(item->fState & MF_MOUSESELECT)) return;
1992 item->fState &= ~MF_MOUSESELECT;
1993 hsubmenu = item->hSubMenu;
1996 submenu = MENU_GetMenu( hsubmenu );
1997 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1998 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1999 DestroyWindow( submenu->hWnd );
2005 /***********************************************************************
2008 * Display the sub-menu of the selected item of this menu.
2009 * Return the handle of the submenu, or hmenu if no submenu to display.
2011 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2012 BOOL selectFirst, UINT wFlags )
2019 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2021 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2023 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2025 item = &menu->items[menu->FocusedItem];
2026 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2029 /* message must be sent before using item,
2030 because nearly everything may be changed by the application ! */
2032 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2033 if (!(wFlags & TPM_NONOTIFY))
2034 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2035 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2037 item = &menu->items[menu->FocusedItem];
2040 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2041 if (!(item->fState & MF_HILITE))
2043 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2044 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2046 SelectObject( hdc, hMenuFont);
2048 item->fState |= MF_HILITE;
2049 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2050 ReleaseDC( menu->hWnd, hdc );
2052 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2055 item->fState |= MF_MOUSESELECT;
2057 if (IS_SYSTEM_MENU(menu))
2059 MENU_InitSysMenuPopup(item->hSubMenu,
2060 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2061 GetClassLongW( menu->hWnd, GCL_STYLE));
2063 NC_GetSysPopupPos( menu->hWnd, &rect );
2064 rect.top = rect.bottom;
2065 rect.right = GetSystemMetrics(SM_CXSIZE);
2066 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2070 GetWindowRect( menu->hWnd, &rect );
2071 if (menu->wFlags & MF_POPUP)
2073 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2074 rect.top += item->rect.top;
2075 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2076 rect.bottom = item->rect.top - item->rect.bottom;
2080 rect.left += item->rect.left;
2081 rect.top += item->rect.bottom;
2082 rect.right = item->rect.right - item->rect.left;
2083 rect.bottom = item->rect.bottom - item->rect.top;
2087 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2088 rect.left, rect.top, rect.right, rect.bottom );
2090 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2091 return item->hSubMenu;
2096 /**********************************************************************
2099 HWND MENU_IsMenuActive(void)
2104 /***********************************************************************
2107 * Walks menu chain trying to find a menu pt maps to.
2109 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2111 POPUPMENU *menu = MENU_GetMenu( hMenu );
2112 UINT item = menu->FocusedItem;
2115 /* try subpopup first (if any) */
2116 ret = (item != NO_SELECTED_ITEM &&
2117 (menu->items[item].fType & MF_POPUP) &&
2118 (menu->items[item].fState & MF_MOUSESELECT))
2119 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2121 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2123 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2124 if( menu->wFlags & MF_POPUP )
2126 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2128 else if (ht == HTSYSMENU)
2129 ret = get_win_sys_menu( menu->hWnd );
2130 else if (ht == HTMENU)
2131 ret = GetMenu( menu->hWnd );
2136 /***********************************************************************
2137 * MENU_ExecFocusedItem
2139 * Execute a menu item (for instance when user pressed Enter).
2140 * Return the wID of the executed item. Otherwise, -1 indicating
2141 * that no menu item was executed;
2142 * Have to receive the flags for the TrackPopupMenu options to avoid
2143 * sending unwanted message.
2146 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2149 POPUPMENU *menu = MENU_GetMenu( hMenu );
2151 TRACE("%p hmenu=%p\n", pmt, hMenu);
2153 if (!menu || !menu->nItems ||
2154 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2156 item = &menu->items[menu->FocusedItem];
2158 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2160 if (!(item->fType & MF_POPUP))
2162 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2164 /* If TPM_RETURNCMD is set you return the id, but
2165 do not send a message to the owner */
2166 if(!(wFlags & TPM_RETURNCMD))
2168 if( menu->wFlags & MF_SYSMENU )
2169 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2170 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2172 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2178 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2183 /***********************************************************************
2184 * MENU_SwitchTracking
2186 * Helper function for menu navigation routines.
2188 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2190 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2191 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2193 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2195 if( pmt->hTopMenu != hPtMenu &&
2196 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2198 /* both are top level menus (system and menu-bar) */
2199 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2200 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2201 pmt->hTopMenu = hPtMenu;
2203 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2204 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2208 /***********************************************************************
2211 * Return TRUE if we can go on with menu tracking.
2213 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2215 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2220 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2223 if( IS_SYSTEM_MENU(ptmenu) )
2224 item = ptmenu->items;
2226 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2230 if( ptmenu->FocusedItem != id )
2231 MENU_SwitchTracking( pmt, hPtMenu, id );
2233 /* If the popup menu is not already "popped" */
2234 if(!(item->fState & MF_MOUSESELECT ))
2236 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2241 /* Else the click was on the menu bar, finish the tracking */
2246 /***********************************************************************
2249 * Return the value of MENU_ExecFocusedItem if
2250 * the selected item was not a popup. Else open the popup.
2251 * A -1 return value indicates that we go on with menu tracking.
2254 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2256 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2261 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2264 if( IS_SYSTEM_MENU(ptmenu) )
2265 item = ptmenu->items;
2267 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2269 if( item && (ptmenu->FocusedItem == id ))
2271 if( !(item->fType & MF_POPUP) )
2272 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2274 /* If we are dealing with the top-level menu */
2275 /* and this is a click on an already "popped" item: */
2276 /* Stop the menu tracking and close the opened submenus */
2277 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2280 ptmenu->bTimeToHide = TRUE;
2286 /***********************************************************************
2289 * Return TRUE if we can go on with menu tracking.
2291 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2293 UINT id = NO_SELECTED_ITEM;
2294 POPUPMENU *ptmenu = NULL;
2298 ptmenu = MENU_GetMenu( hPtMenu );
2299 if( IS_SYSTEM_MENU(ptmenu) )
2302 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2305 if( id == NO_SELECTED_ITEM )
2307 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2308 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2311 else if( ptmenu->FocusedItem != id )
2313 MENU_SwitchTracking( pmt, hPtMenu, id );
2314 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2320 /***********************************************************************
2323 static void MENU_SetCapture( HWND hwnd )
2327 SERVER_START_REQ( set_capture_window )
2330 req->flags = CAPTURE_MENU;
2331 if (!wine_server_call_err( req ))
2333 previous = reply->previous;
2334 hwnd = reply->full_handle;
2339 if (previous && previous != hwnd)
2340 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2344 /***********************************************************************
2347 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2349 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2351 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2353 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2354 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2356 MDINEXTMENU next_menu;
2361 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2362 next_menu.hmenuNext = 0;
2363 next_menu.hwndNext = 0;
2364 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2366 TRACE("%p [%p] -> %p [%p]\n",
2367 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2369 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2371 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2372 hNewWnd = pmt->hOwnerWnd;
2373 if( IS_SYSTEM_MENU(menu) )
2375 /* switch to the menu bar */
2377 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2381 menu = MENU_GetMenu( hNewMenu );
2382 id = menu->nItems - 1;
2385 else if (style & WS_SYSMENU )
2387 /* switch to the system menu */
2388 hNewMenu = get_win_sys_menu( hNewWnd );
2392 else /* application returned a new menu to switch to */
2394 hNewMenu = next_menu.hmenuNext;
2395 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2397 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2399 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2401 if (style & WS_SYSMENU &&
2402 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2404 /* get the real system menu */
2405 hNewMenu = get_win_sys_menu(hNewWnd);
2407 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2409 /* FIXME: Not sure what to do here;
2410 * perhaps try to track hNewMenu as a popup? */
2412 TRACE(" -- got confused.\n");
2419 if( hNewMenu != pmt->hTopMenu )
2421 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2423 if( pmt->hCurrentMenu != pmt->hTopMenu )
2424 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2427 if( hNewWnd != pmt->hOwnerWnd )
2429 pmt->hOwnerWnd = hNewWnd;
2430 MENU_SetCapture( pmt->hOwnerWnd );
2433 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2434 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2441 /***********************************************************************
2444 * The idea is not to show the popup if the next input message is
2445 * going to hide it anyway.
2447 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2451 msg.hwnd = pmt->hOwnerWnd;
2453 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2454 pmt->trackFlags |= TF_SKIPREMOVE;
2459 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2460 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2462 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2463 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2464 if( msg.message == WM_KEYDOWN &&
2465 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2467 pmt->trackFlags |= TF_SUSPENDPOPUP;
2474 /* failures go through this */
2475 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2479 /***********************************************************************
2482 * Handle a VK_ESCAPE key event in a menu.
2484 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2486 BOOL bEndMenu = TRUE;
2488 if (pmt->hCurrentMenu != pmt->hTopMenu)
2490 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2492 if (menu->wFlags & MF_POPUP)
2494 HMENU hmenutmp, hmenuprev;
2496 hmenuprev = hmenutmp = pmt->hTopMenu;
2498 /* close topmost popup */
2499 while (hmenutmp != pmt->hCurrentMenu)
2501 hmenuprev = hmenutmp;
2502 hmenutmp = MENU_GetSubPopup( hmenuprev );
2505 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2506 pmt->hCurrentMenu = hmenuprev;
2514 /***********************************************************************
2517 * Handle a VK_LEFT key event in a menu.
2519 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2522 HMENU hmenutmp, hmenuprev;
2525 hmenuprev = hmenutmp = pmt->hTopMenu;
2526 menu = MENU_GetMenu( hmenutmp );
2528 /* Try to move 1 column left (if possible) */
2529 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2530 NO_SELECTED_ITEM ) {
2532 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2537 /* close topmost popup */
2538 while (hmenutmp != pmt->hCurrentMenu)
2540 hmenuprev = hmenutmp;
2541 hmenutmp = MENU_GetSubPopup( hmenuprev );
2544 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2545 pmt->hCurrentMenu = hmenuprev;
2547 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2549 /* move menu bar selection if no more popups are left */
2551 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2552 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2554 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2556 /* A sublevel menu was displayed - display the next one
2557 * unless there is another displacement coming up */
2559 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2560 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2561 pmt->hTopMenu, TRUE, wFlags);
2567 /***********************************************************************
2570 * Handle a VK_RIGHT key event in a menu.
2572 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2575 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2578 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2580 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2581 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2583 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2585 /* If already displaying a popup, try to display sub-popup */
2587 hmenutmp = pmt->hCurrentMenu;
2588 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2590 /* if subpopup was displayed then we are done */
2591 if (hmenutmp != pmt->hCurrentMenu) return;
2594 /* Check to see if there's another column */
2595 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2596 NO_SELECTED_ITEM ) {
2597 TRACE("Going to %d.\n", nextcol );
2598 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2603 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2605 if( pmt->hCurrentMenu != pmt->hTopMenu )
2607 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2608 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2609 } else hmenutmp = 0;
2611 /* try to move to the next item */
2612 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2613 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2615 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2616 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2617 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2618 pmt->hTopMenu, TRUE, wFlags);
2622 /***********************************************************************
2625 * Menu tracking code.
2627 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2628 HWND hwnd, const RECT *lprect )
2633 INT executedMenuId = -1;
2635 BOOL enterIdleSent = FALSE;
2638 mt.hCurrentMenu = hmenu;
2639 mt.hTopMenu = hmenu;
2640 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2644 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2645 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2646 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2649 if (!(menu = MENU_GetMenu( hmenu )))
2651 WARN("Invalid menu handle %p\n", hmenu);
2652 SetLastError(ERROR_INVALID_MENU_HANDLE);
2656 if (wFlags & TPM_BUTTONDOWN)
2658 /* Get the result in order to start the tracking or not */
2659 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2660 fEndMenu = !fRemove;
2663 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2665 MENU_SetCapture( mt.hOwnerWnd );
2669 menu = MENU_GetMenu( mt.hCurrentMenu );
2670 if (!menu) /* sometimes happens if I do a window manager close */
2673 /* we have to keep the message in the queue until it's
2674 * clear that menu loop is not over yet. */
2678 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2680 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2681 /* remove the message from the queue */
2682 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2688 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2689 enterIdleSent = TRUE;
2690 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2696 /* check if EndMenu() tried to cancel us, by posting this message */
2697 if(msg.message == WM_CANCELMODE)
2699 /* we are now out of the loop */
2702 /* remove the message from the queue */
2703 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2705 /* break out of internal loop, ala ESCAPE */
2709 TranslateMessage( &msg );
2712 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2713 enterIdleSent=FALSE;
2716 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2719 * Use the mouse coordinates in lParam instead of those in the MSG
2720 * struct to properly handle synthetic messages. They are already
2721 * in screen coordinates.
2723 mt.pt.x = (short)LOWORD(msg.lParam);
2724 mt.pt.y = (short)HIWORD(msg.lParam);
2726 /* Find a menu for this mouse event */
2727 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2731 /* no WM_NC... messages in captured state */
2733 case WM_RBUTTONDBLCLK:
2734 case WM_RBUTTONDOWN:
2735 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2737 case WM_LBUTTONDBLCLK:
2738 case WM_LBUTTONDOWN:
2739 /* If the message belongs to the menu, removes it from the queue */
2740 /* Else, end menu tracking */
2741 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2742 fEndMenu = !fRemove;
2746 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2749 /* Check if a menu was selected by the mouse */
2752 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2754 /* End the loop if executedMenuId is an item ID */
2755 /* or if the job was done (executedMenuId = 0). */
2756 fEndMenu = fRemove = (executedMenuId != -1);
2758 /* No menu was selected by the mouse */
2759 /* if the function was called by TrackPopupMenu, continue
2760 with the menu tracking. If not, stop it */
2762 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2767 /* the selected menu item must be changed every time */
2768 /* the mouse moves. */
2771 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2773 } /* switch(msg.message) - mouse */
2775 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2777 fRemove = TRUE; /* Keyboard messages are always removed */
2790 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2791 NO_SELECTED_ITEM, FALSE, 0 );
2794 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2795 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2798 case VK_DOWN: /* If on menu bar, pull-down the menu */
2800 menu = MENU_GetMenu( mt.hCurrentMenu );
2801 if (!(menu->wFlags & MF_POPUP))
2802 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2803 else /* otherwise try to move selection */
2804 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2808 MENU_KeyLeft( &mt, wFlags );
2812 MENU_KeyRight( &mt, wFlags );
2816 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2822 hi.cbSize = sizeof(HELPINFO);
2823 hi.iContextType = HELPINFO_MENUITEM;
2824 if (menu->FocusedItem == NO_SELECTED_ITEM)
2827 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2828 hi.hItemHandle = hmenu;
2829 hi.dwContextId = menu->dwContextHelpID;
2830 hi.MousePos = msg.pt;
2831 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2838 break; /* WM_KEYDOWN */
2845 if (msg.wParam == '\r' || msg.wParam == ' ')
2847 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2848 fEndMenu = (executedMenuId != -1);
2853 /* Hack to avoid control chars. */
2854 /* We will find a better way real soon... */
2855 if (msg.wParam < 32) break;
2857 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2858 LOWORD(msg.wParam), FALSE );
2859 if (pos == (UINT)-2) fEndMenu = TRUE;
2860 else if (pos == (UINT)-1) MessageBeep(0);
2863 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2865 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2866 fEndMenu = (executedMenuId != -1);
2870 } /* switch(msg.message) - kbd */
2874 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2875 DispatchMessageW( &msg );
2879 if (!fEndMenu) fRemove = TRUE;
2881 /* finally remove message from the queue */
2883 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2884 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2885 else mt.trackFlags &= ~TF_SKIPREMOVE;
2888 MENU_SetCapture(0); /* release the capture */
2890 /* If dropdown is still painted and the close box is clicked on
2891 then the menu will be destroyed as part of the DispatchMessage above.
2892 This will then invalidate the menu handle in mt.hTopMenu. We should
2893 check for this first. */
2894 if( IsMenu( mt.hTopMenu ) )
2896 menu = MENU_GetMenu( mt.hTopMenu );
2898 if( IsWindow( mt.hOwnerWnd ) )
2900 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2902 if (menu && (menu->wFlags & MF_POPUP))
2904 DestroyWindow( menu->hWnd );
2907 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2908 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2911 /* Reset the variable for hiding menu */
2912 if( menu ) menu->bTimeToHide = FALSE;
2915 /* The return value is only used by TrackPopupMenu */
2916 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2917 if (executedMenuId == -1) executedMenuId = 0;
2918 return executedMenuId;
2921 /***********************************************************************
2924 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2928 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2932 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2933 if (!(wFlags & TPM_NONOTIFY))
2934 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2936 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2938 if (!(wFlags & TPM_NONOTIFY))
2940 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2941 /* If an app changed/recreated menu bar entries in WM_INITMENU
2942 * menu sizes will be recalculated once the menu created/shown.
2946 /* This makes the menus of applications built with Delphi work.
2947 * It also enables menus to be displayed in more than one window,
2948 * but there are some bugs left that need to be fixed in this case.
2950 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2954 /***********************************************************************
2957 static BOOL MENU_ExitTracking(HWND hWnd)
2959 TRACE("hwnd=%p\n", hWnd);
2961 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2967 /***********************************************************************
2968 * MENU_TrackMouseMenuBar
2970 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2972 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2974 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2975 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2977 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2981 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2982 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2983 MENU_ExitTracking(hWnd);
2988 /***********************************************************************
2989 * MENU_TrackKbdMenuBar
2991 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2993 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
2995 UINT uItem = NO_SELECTED_ITEM;
2997 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2999 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3001 /* find window that has a menu */
3003 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3004 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3006 /* check if we have to track a system menu */
3008 hTrackMenu = GetMenu( hwnd );
3009 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3011 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3012 hTrackMenu = get_win_sys_menu( hwnd );
3014 wParam |= HTSYSMENU; /* prevent item lookup */
3017 if (!IsMenu( hTrackMenu )) return;
3019 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3021 if( wChar && wChar != ' ' )
3023 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3024 if ( uItem >= (UINT)(-2) )
3026 if( uItem == (UINT)(-1) ) MessageBeep(0);
3027 /* schedule end of menu tracking */
3028 wFlags |= TF_ENDMENU;
3033 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3035 if (wParam & HTSYSMENU)
3037 /* prevent sysmenu activation for managed windows on Alt down/up */
3038 if (GetPropA( hwnd, "__wine_x11_managed" ))
3039 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3043 if( uItem == NO_SELECTED_ITEM )
3044 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3046 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3050 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3051 MENU_ExitTracking( hwnd );
3055 /**********************************************************************
3056 * TrackPopupMenu (USER32.@)
3058 * Like the win32 API, the function return the command ID only if the
3059 * flag TPM_RETURNCMD is on.
3062 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3063 INT nReserved, HWND hWnd, const RECT *lpRect )
3067 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3069 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3070 if (!(wFlags & TPM_NONOTIFY))
3071 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3073 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3074 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3075 MENU_ExitTracking(hWnd);
3080 /**********************************************************************
3081 * TrackPopupMenuEx (USER32.@)
3083 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3084 HWND hWnd, LPTPMPARAMS lpTpm )
3086 FIXME("not fully implemented\n" );
3087 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3088 lpTpm ? &lpTpm->rcExclude : NULL );
3091 /***********************************************************************
3094 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3096 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3098 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3104 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3105 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3109 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3110 return MA_NOACTIVATE;
3115 BeginPaint( hwnd, &ps );
3116 MENU_DrawPopupMenu( hwnd, ps.hdc,
3117 (HMENU)GetWindowLongW( hwnd, 0 ) );
3118 EndPaint( hwnd, &ps );
3125 /* zero out global pointer in case resident popup window was destroyed. */
3126 if (hwnd == top_popup) top_popup = 0;
3133 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3136 SetWindowLongW( hwnd, 0, 0 );
3139 case MM_SETMENUHANDLE:
3140 SetWindowLongW( hwnd, 0, wParam );
3143 case MM_GETMENUHANDLE:
3144 return GetWindowLongW( hwnd, 0 );
3147 return DefWindowProcW( hwnd, message, wParam, lParam );
3153 /***********************************************************************
3154 * MENU_GetMenuBarHeight
3156 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3158 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3159 INT orgX, INT orgY )
3165 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3167 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3169 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3170 SelectObject( hdc, hMenuFont);
3171 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3172 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3173 ReleaseDC( hwnd, hdc );
3174 return lppop->Height;
3178 /*******************************************************************
3179 * ChangeMenuA (USER32.@)
3181 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3182 UINT id, UINT flags )
3184 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3185 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3187 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3188 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3190 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3191 flags & MF_BYPOSITION ? pos : id,
3192 flags & ~MF_REMOVE );
3193 /* Default: MF_INSERT */
3194 return InsertMenuA( hMenu, pos, flags, id, data );
3198 /*******************************************************************
3199 * ChangeMenuW (USER32.@)
3201 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3202 UINT id, UINT flags )
3204 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3205 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3207 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3208 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3210 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3211 flags & MF_BYPOSITION ? pos : id,
3212 flags & ~MF_REMOVE );
3213 /* Default: MF_INSERT */
3214 return InsertMenuW( hMenu, pos, flags, id, data );
3218 /*******************************************************************
3219 * CheckMenuItem (USER32.@)
3221 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3226 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3227 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3228 ret = item->fState & MF_CHECKED;
3229 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3230 else item->fState &= ~MF_CHECKED;
3235 /**********************************************************************
3236 * EnableMenuItem (USER32.@)
3238 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3244 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3246 /* Get the Popupmenu to access the owner menu */
3247 if (!(menu = MENU_GetMenu(hMenu)))
3250 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3253 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3254 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3256 /* If the close item in the system menu change update the close button */
3257 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3259 if (menu->hSysMenuOwner != 0)
3262 POPUPMENU* parentMenu;
3264 /* Get the parent menu to access*/
3265 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3268 /* Refresh the frame to reflect the change */
3269 GetWindowRect(parentMenu->hWnd, &rc);
3270 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3272 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3280 /*******************************************************************
3281 * GetMenuStringA (USER32.@)
3283 INT WINAPI GetMenuStringA(
3284 HMENU hMenu, /* [in] menuhandle */
3285 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3286 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3287 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3288 UINT wFlags /* [in] MF_ flags */
3292 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3293 if (str && nMaxSiz) str[0] = '\0';
3294 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3295 if (!IS_STRING_ITEM(item->fType)) return 0;
3296 if (!str || !nMaxSiz) return strlenW(item->text);
3297 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3299 TRACE("returning '%s'\n", str );
3304 /*******************************************************************
3305 * GetMenuStringW (USER32.@)
3307 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3308 LPWSTR str, INT nMaxSiz, UINT wFlags )
3312 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3313 if (str && nMaxSiz) str[0] = '\0';
3314 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3315 if (!IS_STRING_ITEM(item->fType)) return 0;
3316 if (!str || !nMaxSiz) return strlenW(item->text);
3317 lstrcpynW( str, item->text, nMaxSiz );
3318 return strlenW(str);
3322 /**********************************************************************
3323 * HiliteMenuItem (USER32.@)
3325 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3329 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3330 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3331 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3332 if (menu->FocusedItem == wItemID) return TRUE;
3333 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3334 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3339 /**********************************************************************
3340 * GetMenuState (USER32.@)
3342 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3345 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3346 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3347 debug_print_menuitem (" item: ", item, "");
3348 if (item->fType & MF_POPUP)
3350 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3351 if (!menu) return -1;
3352 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3356 /* We used to (from way back then) mask the result to 0xff. */
3357 /* I don't know why and it seems wrong as the documented */
3358 /* return flag MF_SEPARATOR is outside that mask. */
3359 return (item->fType | item->fState);
3364 /**********************************************************************
3365 * GetMenuItemCount (USER32.@)
3367 INT WINAPI GetMenuItemCount( HMENU hMenu )
3369 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3370 if (!menu) return -1;
3371 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3372 return menu->nItems;
3376 /**********************************************************************
3377 * GetMenuItemID (USER32.@)
3379 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3383 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3384 if (lpmi->fType & MF_POPUP) return -1;
3390 /*******************************************************************
3391 * InsertMenuW (USER32.@)
3393 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3394 UINT_PTR id, LPCWSTR str )
3398 if (IS_STRING_ITEM(flags) && str)
3399 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3400 hMenu, pos, flags, id, debugstr_w(str) );
3401 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3402 hMenu, pos, flags, id, (DWORD)str );
3404 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3406 if (!(MENU_SetItemData( item, flags, id, str )))
3408 RemoveMenu( hMenu, pos, flags );
3412 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3413 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3415 item->hCheckBit = item->hUnCheckBit = 0;
3420 /*******************************************************************
3421 * InsertMenuA (USER32.@)
3423 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3424 UINT_PTR id, LPCSTR str )
3428 if (IS_STRING_ITEM(flags) && str)
3430 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3431 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3434 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3435 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3436 HeapFree( GetProcessHeap(), 0, newstr );
3440 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3444 /*******************************************************************
3445 * AppendMenuA (USER32.@)
3447 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3448 UINT_PTR id, LPCSTR data )
3450 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3454 /*******************************************************************
3455 * AppendMenuW (USER32.@)
3457 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3458 UINT_PTR id, LPCWSTR data )
3460 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3464 /**********************************************************************
3465 * RemoveMenu (USER32.@)
3467 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3472 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3473 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3474 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3478 MENU_FreeItemData( item );
3480 if (--menu->nItems == 0)
3482 HeapFree( GetProcessHeap(), 0, menu->items );
3487 while(nPos < menu->nItems)
3493 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3494 menu->nItems * sizeof(MENUITEM) );
3500 /**********************************************************************
3501 * DeleteMenu (USER32.@)
3503 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3505 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3506 if (!item) return FALSE;
3507 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3508 /* nPos is now the position of the item */
3509 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3514 /*******************************************************************
3515 * ModifyMenuW (USER32.@)
3517 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3518 UINT_PTR id, LPCWSTR str )
3522 if (IS_STRING_ITEM(flags))
3524 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3528 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3531 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3532 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3533 return MENU_SetItemData( item, flags, id, str );
3537 /*******************************************************************
3538 * ModifyMenuA (USER32.@)
3540 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3541 UINT_PTR id, LPCSTR str )
3545 if (IS_STRING_ITEM(flags) && str)
3547 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3548 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3551 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3552 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3553 HeapFree( GetProcessHeap(), 0, newstr );
3557 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3561 /**********************************************************************
3562 * CreatePopupMenu (USER32.@)
3564 HMENU WINAPI CreatePopupMenu(void)
3569 if (!(hmenu = CreateMenu())) return 0;
3570 menu = MENU_GetMenu( hmenu );
3571 menu->wFlags |= MF_POPUP;
3572 menu->bTimeToHide = FALSE;
3577 /**********************************************************************
3578 * GetMenuCheckMarkDimensions (USER.417)
3579 * GetMenuCheckMarkDimensions (USER32.@)
3581 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3583 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3587 /**********************************************************************
3588 * SetMenuItemBitmaps (USER32.@)
3590 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3591 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3594 TRACE("(%p, %04x, %04x, %p, %p)\n",
3595 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3596 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3598 if (!hNewCheck && !hNewUnCheck)
3600 item->fState &= ~MF_USECHECKBITMAPS;
3602 else /* Install new bitmaps */
3604 item->hCheckBit = hNewCheck;
3605 item->hUnCheckBit = hNewUnCheck;
3606 item->fState |= MF_USECHECKBITMAPS;
3612 /**********************************************************************
3613 * CreateMenu (USER32.@)
3615 HMENU WINAPI CreateMenu(void)
3619 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3620 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3622 ZeroMemory(menu, sizeof(POPUPMENU));
3623 menu->wMagic = MENU_MAGIC;
3624 menu->FocusedItem = NO_SELECTED_ITEM;
3625 menu->bTimeToHide = FALSE;
3627 TRACE("return %p\n", hMenu );
3633 /**********************************************************************
3634 * DestroyMenu (USER32.@)
3636 BOOL WINAPI DestroyMenu( HMENU hMenu )
3638 TRACE("(%p)\n", hMenu);
3640 /* Silently ignore attempts to destroy default system popup */
3642 if (hMenu && hMenu != MENU_DefSysPopup)
3644 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3646 if (!lppop) return FALSE;
3648 lppop->wMagic = 0; /* Mark it as destroyed */
3650 /* DestroyMenu should not destroy system menu popup owner */
3651 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3653 DestroyWindow( lppop->hWnd );
3657 if (lppop->items) /* recursively destroy submenus */
3660 MENUITEM *item = lppop->items;
3661 for (i = lppop->nItems; i > 0; i--, item++)
3663 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3664 MENU_FreeItemData( item );
3666 HeapFree( GetProcessHeap(), 0, lppop->items );
3668 USER_HEAP_FREE( hMenu );
3670 return (hMenu != MENU_DefSysPopup);
3674 /**********************************************************************
3675 * GetSystemMenu (USER32.@)
3677 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3679 WND *wndPtr = WIN_GetPtr( hWnd );
3682 if (wndPtr == WND_DESKTOP) return 0;
3683 if (wndPtr == WND_OTHER_PROCESS)
3685 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3689 if( wndPtr->hSysMenu )
3693 DestroyMenu(wndPtr->hSysMenu);
3694 wndPtr->hSysMenu = 0;
3698 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3701 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3702 menu->items[0].hSubMenu = MENU_CopySysPopup();
3706 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3707 wndPtr->hSysMenu, hWnd);
3708 wndPtr->hSysMenu = 0;
3713 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3714 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3716 if( wndPtr->hSysMenu )
3719 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3721 /* Store the dummy sysmenu handle to facilitate the refresh */
3722 /* of the close button if the SC_CLOSE item change */
3723 menu = MENU_GetMenu(retvalue);
3725 menu->hSysMenuOwner = wndPtr->hSysMenu;
3727 WIN_ReleasePtr( wndPtr );
3729 return bRevert ? 0 : retvalue;
3733 /*******************************************************************
3734 * SetSystemMenu (USER32.@)
3736 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3738 WND *wndPtr = WIN_GetPtr( hwnd );
3740 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3742 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3743 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3744 WIN_ReleasePtr( wndPtr );
3751 /**********************************************************************
3752 * GetMenu (USER32.@)
3754 HMENU WINAPI GetMenu( HWND hWnd )
3756 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3757 TRACE("for %p returning %p\n", hWnd, retvalue);
3762 /**********************************************************************
3765 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3766 * SetWindowPos call that would result if SetMenu were called directly.
3768 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3770 TRACE("(%p, %p);\n", hWnd, hMenu);
3772 if (hMenu && !IsMenu(hMenu))
3774 WARN("hMenu %p is not a menu handle\n", hMenu);
3777 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3780 hWnd = WIN_GetFullHandle( hWnd );
3781 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3787 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3789 lpmenu->hWnd = hWnd;
3790 lpmenu->Height = 0; /* Make sure we recalculate the size */
3792 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3797 /**********************************************************************
3798 * SetMenu (USER32.@)
3800 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3802 if(!MENU_SetMenu(hWnd, hMenu))
3805 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3806 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3811 /**********************************************************************
3812 * GetSubMenu (USER32.@)
3814 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3818 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3819 if (!(lpmi->fType & MF_POPUP)) return 0;
3820 return lpmi->hSubMenu;
3824 /**********************************************************************
3825 * DrawMenuBar (USER32.@)
3827 BOOL WINAPI DrawMenuBar( HWND hWnd )
3830 HMENU hMenu = GetMenu(hWnd);
3832 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3834 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3836 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3837 lppop->hwndOwner = hWnd;
3838 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3839 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3843 /***********************************************************************
3844 * DrawMenuBarTemp (USER32.@)
3848 * called by W98SE desk.cpl Control Panel Applet
3850 * Not 100% sure about the param names, but close.
3852 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3859 hMenu = GetMenu(hwnd);
3864 lppop = MENU_GetMenu( hMenu );
3865 if (lppop == NULL || lprect == NULL)
3867 retvalue = GetSystemMetrics(SM_CYMENU);
3871 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3873 hfontOld = SelectObject( hDC, hFont);
3875 if (lppop->Height == 0)
3876 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3878 lprect->bottom = lprect->top + lppop->Height;
3880 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
3882 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3883 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3884 LineTo( hDC, lprect->right, lprect->bottom );
3886 if (lppop->nItems == 0)
3888 retvalue = GetSystemMetrics(SM_CYMENU);
3892 for (i = 0; i < lppop->nItems; i++)
3894 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3895 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3897 retvalue = lppop->Height;
3900 if (hfontOld) SelectObject (hDC, hfontOld);
3904 /***********************************************************************
3905 * EndMenu (USER.187)
3906 * EndMenu (USER32.@)
3908 void WINAPI EndMenu(void)
3910 /* if we are in the menu code, and it is active */
3911 if (!fEndMenu && top_popup)
3913 /* terminate the menu handling code */
3916 /* needs to be posted to wakeup the internal menu handler */
3917 /* which will now terminate the menu, in the event that */
3918 /* the main window was minimized, or lost focus, so we */
3919 /* don't end up with an orphaned menu */
3920 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3925 /***********************************************************************
3926 * LookupMenuHandle (USER.217)
3928 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3930 HMENU hmenu32 = HMENU_32(hmenu);
3932 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3933 else return HMENU_16(hmenu32);
3937 /**********************************************************************
3938 * LoadMenu (USER.150)
3940 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3946 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3947 if (!name) return 0;
3949 instance = GetExePtr( instance );
3950 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3951 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3952 hMenu = LoadMenuIndirect16(LockResource16(handle));
3953 FreeResource16( handle );
3958 /*****************************************************************
3959 * LoadMenuA (USER32.@)
3961 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3963 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3964 if (!hrsrc) return 0;
3965 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3969 /*****************************************************************
3970 * LoadMenuW (USER32.@)
3972 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3974 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3975 if (!hrsrc) return 0;
3976 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3980 /**********************************************************************
3981 * LoadMenuIndirect (USER.220)
3983 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3986 WORD version, offset;
3987 LPCSTR p = (LPCSTR)template;
3989 TRACE("(%p)\n", template );
3990 version = GET_WORD(p);
3994 WARN("version must be 0 for Win16\n" );
3997 offset = GET_WORD(p);
3998 p += sizeof(WORD) + offset;
3999 if (!(hMenu = CreateMenu())) return 0;
4000 if (!MENU_ParseResource( p, hMenu, FALSE ))
4002 DestroyMenu( hMenu );
4005 return HMENU_16(hMenu);
4009 /**********************************************************************
4010 * LoadMenuIndirectW (USER32.@)
4012 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4015 WORD version, offset;
4016 LPCSTR p = (LPCSTR)template;
4018 version = GET_WORD(p);
4020 TRACE("%p, ver %d\n", template, version );
4023 case 0: /* standard format is version of 0 */
4024 offset = GET_WORD(p);
4025 p += sizeof(WORD) + offset;
4026 if (!(hMenu = CreateMenu())) return 0;
4027 if (!MENU_ParseResource( p, hMenu, TRUE ))
4029 DestroyMenu( hMenu );
4033 case 1: /* extended format is version of 1 */
4034 offset = GET_WORD(p);
4035 p += sizeof(WORD) + offset;
4036 if (!(hMenu = CreateMenu())) return 0;
4037 if (!MENUEX_ParseResource( p, hMenu))
4039 DestroyMenu( hMenu );
4044 ERR("version %d not supported.\n", version);
4050 /**********************************************************************
4051 * LoadMenuIndirectA (USER32.@)
4053 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4055 return LoadMenuIndirectW( template );
4059 /**********************************************************************
4062 BOOL WINAPI IsMenu(HMENU hmenu)
4064 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4065 return menu != NULL;
4068 /**********************************************************************
4069 * GetMenuItemInfo_common
4072 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4073 LPMENUITEMINFOW lpmii, BOOL unicode)
4075 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4077 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4082 if (lpmii->fMask & MIIM_TYPE) {
4083 lpmii->fType = menu->fType;
4084 switch (MENU_ITEM_TYPE(menu->fType)) {
4086 break; /* will be done below */
4089 lpmii->dwTypeData = menu->text;
4096 /* copy the text string */
4097 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4098 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4103 len = strlenW(menu->text);
4104 if(lpmii->dwTypeData && lpmii->cch)
4105 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4109 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4110 if(lpmii->dwTypeData && lpmii->cch)
4111 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4112 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4113 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4115 /* if we've copied a substring we return its length */
4116 if(lpmii->dwTypeData && lpmii->cch)
4118 if (lpmii->cch <= len) lpmii->cch--;
4120 else /* return length of string */
4124 if (lpmii->fMask & MIIM_FTYPE)
4125 lpmii->fType = menu->fType;
4127 if (lpmii->fMask & MIIM_BITMAP)
4128 lpmii->hbmpItem = menu->hbmpItem;
4130 if (lpmii->fMask & MIIM_STATE)
4131 lpmii->fState = menu->fState;
4133 if (lpmii->fMask & MIIM_ID)
4134 lpmii->wID = menu->wID;
4136 if (lpmii->fMask & MIIM_SUBMENU)
4137 lpmii->hSubMenu = menu->hSubMenu;
4139 if (lpmii->fMask & MIIM_CHECKMARKS) {
4140 lpmii->hbmpChecked = menu->hCheckBit;
4141 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4143 if (lpmii->fMask & MIIM_DATA)
4144 lpmii->dwItemData = menu->dwItemData;
4149 /**********************************************************************
4150 * GetMenuItemInfoA (USER32.@)
4152 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4153 LPMENUITEMINFOA lpmii)
4155 return GetMenuItemInfo_common (hmenu, item, bypos,
4156 (LPMENUITEMINFOW)lpmii, FALSE);
4159 /**********************************************************************
4160 * GetMenuItemInfoW (USER32.@)
4162 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4163 LPMENUITEMINFOW lpmii)
4165 return GetMenuItemInfo_common (hmenu, item, bypos,
4170 /* set a menu item text from a ASCII or Unicode string */
4171 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4176 menu->fType |= MF_SEPARATOR;
4180 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4181 strcpyW( menu->text, text );
4185 LPCSTR str = (LPCSTR)text;
4186 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4187 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4188 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4193 /**********************************************************************
4194 * SetMenuItemInfo_common
4197 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4198 const MENUITEMINFOW *lpmii,
4201 if (!menu) return FALSE;
4203 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4205 if (lpmii->fMask & MIIM_TYPE ) {
4206 /* Get rid of old string. */
4207 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4208 HeapFree(GetProcessHeap(), 0, menu->text);
4212 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4213 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4214 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4216 menu->text = lpmii->dwTypeData;
4218 if (IS_STRING_ITEM(menu->fType))
4219 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4222 if (lpmii->fMask & MIIM_FTYPE ) {
4223 /* free the string when the type is changing */
4224 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4225 HeapFree(GetProcessHeap(), 0, menu->text);
4228 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4229 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4230 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4231 menu->fType |= MF_SEPARATOR;
4234 if (lpmii->fMask & MIIM_STRING ) {
4235 if (IS_STRING_ITEM(menu->fType)) {
4236 /* free the string when used */
4237 HeapFree(GetProcessHeap(), 0, menu->text);
4238 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4242 if (lpmii->fMask & MIIM_STATE)
4244 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4245 menu->fState = lpmii->fState;
4248 if (lpmii->fMask & MIIM_ID)
4249 menu->wID = lpmii->wID;
4251 if (lpmii->fMask & MIIM_SUBMENU) {
4252 menu->hSubMenu = lpmii->hSubMenu;
4253 if (menu->hSubMenu) {
4254 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4256 subMenu->wFlags |= MF_POPUP;
4257 menu->fType |= MF_POPUP;
4260 /* FIXME: Return an error ? */
4261 menu->fType &= ~MF_POPUP;
4264 menu->fType &= ~MF_POPUP;
4267 if (lpmii->fMask & MIIM_CHECKMARKS)
4269 if (lpmii->fType & MFT_RADIOCHECK)
4270 menu->fType |= MFT_RADIOCHECK;
4272 menu->hCheckBit = lpmii->hbmpChecked;
4273 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4275 if (lpmii->fMask & MIIM_DATA)
4276 menu->dwItemData = lpmii->dwItemData;
4278 if (lpmii->fMask & MIIM_BITMAP)
4279 menu->hbmpItem = lpmii->hbmpItem;
4281 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4285 /**********************************************************************
4286 * SetMenuItemInfoA (USER32.@)
4288 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4289 const MENUITEMINFOA *lpmii)
4291 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4292 (const MENUITEMINFOW *)lpmii, FALSE);
4295 /**********************************************************************
4296 * SetMenuItemInfoW (USER32.@)
4298 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4299 const MENUITEMINFOW *lpmii)
4301 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4305 /**********************************************************************
4306 * SetMenuDefaultItem (USER32.@)
4309 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4315 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4317 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4319 /* reset all default-item flags */
4321 for (i = 0; i < menu->nItems; i++, item++)
4323 item->fState &= ~MFS_DEFAULT;
4326 /* no default item */
4335 if ( uItem >= menu->nItems ) return FALSE;
4336 item[uItem].fState |= MFS_DEFAULT;
4341 for (i = 0; i < menu->nItems; i++, item++)
4343 if (item->wID == uItem)
4345 item->fState |= MFS_DEFAULT;
4354 /**********************************************************************
4355 * GetMenuDefaultItem (USER32.@)
4357 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4363 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4365 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4367 /* find default item */
4371 if (! item) return -1;
4373 while ( !( item->fState & MFS_DEFAULT ) )
4376 if (i >= menu->nItems ) return -1;
4379 /* default: don't return disabled items */
4380 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4382 /* search rekursiv when needed */
4383 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4386 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4387 if ( -1 != ret ) return ret;
4389 /* when item not found in submenu, return the popup item */
4391 return ( bypos ) ? i : item->wID;
4396 /**********************************************************************
4397 * InsertMenuItemA (USER32.@)
4399 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4400 const MENUITEMINFOA *lpmii)
4402 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4403 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4407 /**********************************************************************
4408 * InsertMenuItemW (USER32.@)
4410 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4411 const MENUITEMINFOW *lpmii)
4413 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4414 return SetMenuItemInfo_common(item, lpmii, TRUE);
4417 /**********************************************************************
4418 * CheckMenuRadioItem (USER32.@)
4421 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4422 UINT first, UINT last, UINT check,
4425 MENUITEM *mifirst, *milast, *micheck;
4426 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4428 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4430 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4431 milast = MENU_FindItem (&mlast, &last, bypos);
4432 micheck = MENU_FindItem (&mcheck, &check, bypos);
4434 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4435 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4436 micheck > milast || micheck < mifirst)
4439 while (mifirst <= milast)
4441 if (mifirst == micheck)
4443 mifirst->fType |= MFT_RADIOCHECK;
4444 mifirst->fState |= MFS_CHECKED;
4446 mifirst->fType &= ~MFT_RADIOCHECK;
4447 mifirst->fState &= ~MFS_CHECKED;
4456 /**********************************************************************
4457 * GetMenuItemRect (USER32.@)
4459 * ATTENTION: Here, the returned values in rect are the screen
4460 * coordinates of the item just like if the menu was
4461 * always on the upper left side of the application.
4464 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4467 POPUPMENU *itemMenu;
4471 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4473 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4474 referenceHwnd = hwnd;
4478 itemMenu = MENU_GetMenu(hMenu);
4479 if (itemMenu == NULL)
4482 if(itemMenu->hWnd == 0)
4484 referenceHwnd = itemMenu->hWnd;
4487 if ((rect == NULL) || (item == NULL))
4492 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4498 /**********************************************************************
4499 * SetMenuInfo (USER32.@)
4502 * MIM_APPLYTOSUBMENUS
4503 * actually use the items to draw the menu
4505 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4509 TRACE("(%p %p)\n", hMenu, lpmi);
4511 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4514 if (lpmi->fMask & MIM_BACKGROUND)
4515 menu->hbrBack = lpmi->hbrBack;
4517 if (lpmi->fMask & MIM_HELPID)
4518 menu->dwContextHelpID = lpmi->dwContextHelpID;
4520 if (lpmi->fMask & MIM_MAXHEIGHT)
4521 menu->cyMax = lpmi->cyMax;
4523 if (lpmi->fMask & MIM_MENUDATA)
4524 menu->dwMenuData = lpmi->dwMenuData;
4526 if (lpmi->fMask & MIM_STYLE)
4528 menu->dwStyle = lpmi->dwStyle;
4529 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4530 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4531 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4532 if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4533 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4541 /**********************************************************************
4542 * GetMenuInfo (USER32.@)
4548 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4551 TRACE("(%p %p)\n", hMenu, lpmi);
4553 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4556 if (lpmi->fMask & MIM_BACKGROUND)
4557 lpmi->hbrBack = menu->hbrBack;
4559 if (lpmi->fMask & MIM_HELPID)
4560 lpmi->dwContextHelpID = menu->dwContextHelpID;
4562 if (lpmi->fMask & MIM_MAXHEIGHT)
4563 lpmi->cyMax = menu->cyMax;
4565 if (lpmi->fMask & MIM_MENUDATA)
4566 lpmi->dwMenuData = menu->dwMenuData;
4568 if (lpmi->fMask & MIM_STYLE)
4569 lpmi->dwStyle = menu->dwStyle;
4577 /**********************************************************************
4578 * SetMenuContextHelpId (USER32.@)
4580 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4584 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4586 if ((menu = MENU_GetMenu(hMenu)))
4588 menu->dwContextHelpID = dwContextHelpID;
4595 /**********************************************************************
4596 * GetMenuContextHelpId (USER32.@)
4598 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4602 TRACE("(%p)\n", hMenu);
4604 if ((menu = MENU_GetMenu(hMenu)))
4606 return menu->dwContextHelpID;
4611 /**********************************************************************
4612 * MenuItemFromPoint (USER32.@)
4614 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4616 POPUPMENU *menu = MENU_GetMenu(hMenu);
4619 /*FIXME: Do we have to handle hWnd here? */
4620 if (!menu) return -1;
4621 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4626 /**********************************************************************
4627 * translate_accelerator
4629 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4630 BYTE fVirt, WORD key, WORD cmd )
4635 if (wParam != key) return FALSE;
4637 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4638 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4639 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4641 if (message == WM_CHAR || message == WM_SYSCHAR)
4643 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4645 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4651 if(fVirt & FVIRTKEY)
4653 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4654 wParam, 0xff & HIWORD(lParam));
4656 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4657 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4661 if (!(lParam & 0x01000000)) /* no special_key */
4663 if ((fVirt & FALT) && (lParam & 0x20000000))
4664 { /* ^^ ALT pressed */
4665 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4674 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4678 HMENU hMenu, hSubMenu, hSysMenu;
4679 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4681 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4682 hSysMenu = get_win_sys_menu( hWnd );
4684 /* find menu item and ask application to initialize it */
4685 /* 1. in the system menu */
4686 hSubMenu = hSysMenu;
4688 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4692 if (!IsWindowEnabled(hWnd))
4696 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4697 if(hSubMenu != hSysMenu)
4699 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4700 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4701 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4703 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4706 else /* 2. in the window's menu */
4710 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4714 if (!IsWindowEnabled(hWnd))
4718 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4719 if(hSubMenu != hMenu)
4721 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4722 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4723 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4725 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4732 if (uSysStat != (UINT)-1)
4734 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4741 if (uStat != (UINT)-1)
4747 if (uStat & (MF_DISABLED|MF_GRAYED))
4759 if( mesg==WM_COMMAND )
4761 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4762 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4764 else if( mesg==WM_SYSCOMMAND )
4766 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4767 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4771 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4772 * #0: unknown (please report!)
4773 * #1: for WM_KEYUP,WM_SYSKEYUP
4774 * #2: mouse is captured
4775 * #3: window is disabled
4776 * #4: it's a disabled system menu option
4777 * #5: it's a menu option, but window is iconic
4778 * #6: it's a menu option, but disabled
4780 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4782 ERR_(accel)(" unknown reason - please report!\n");
4787 /**********************************************************************
4788 * TranslateAccelerator (USER32.@)
4789 * TranslateAcceleratorA (USER32.@)
4791 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4794 LPACCEL16 lpAccelTbl;
4798 if (!hWnd || !msg) return 0;
4800 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4802 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4806 wParam = msg->wParam;
4808 switch (msg->message)
4817 char ch = LOWORD(wParam);
4819 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4820 wParam = MAKEWPARAM(wch, HIWORD(wParam));
4828 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4829 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4833 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4834 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4836 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4841 /**********************************************************************
4842 * TranslateAcceleratorW (USER32.@)
4844 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4847 LPACCEL16 lpAccelTbl;
4850 if (!hWnd || !msg) return 0;
4852 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4854 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4858 switch (msg->message)
4870 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4871 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4875 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4876 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4878 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);