kerberos: Move support for SpMakeSignature to the Unix library.
[wine.git] / dlls / user32 / menu.c
blobecefe9dd67cba23585b8ced390f9d46e604847cf
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include <stdarg.h>
43 #include <string.h>
45 #define OEMRESOURCE
47 #include "windef.h"
48 #include "winbase.h"
49 #include "wingdi.h"
50 #include "winnls.h"
51 #include "wine/server.h"
52 #include "wine/exception.h"
53 #include "win.h"
54 #include "controls.h"
55 #include "user_private.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(menu);
59 WINE_DECLARE_DEBUG_CHANNEL(accel);
61 /* Menu item structure */
62 typedef struct {
63 /* ----------- MENUITEMINFO Stuff ----------- */
64 UINT fType; /* Item type. */
65 UINT fState; /* Item state. */
66 UINT_PTR wID; /* Item id. */
67 HMENU hSubMenu; /* Pop-up menu. */
68 HBITMAP hCheckBit; /* Bitmap when checked. */
69 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
70 LPWSTR text; /* Item text. */
71 ULONG_PTR dwItemData; /* Application defined. */
72 LPWSTR dwTypeData; /* depends on fMask */
73 HBITMAP hbmpItem; /* bitmap */
74 /* ----------- Wine stuff ----------- */
75 RECT rect; /* Item area (relative to the items_rect).
76 * See MENU_AdjustMenuItemRect(). */
77 UINT xTab; /* X position of text after Tab */
78 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
79 * bitmap */
80 } MENUITEM;
82 /* Popup menu structure */
83 typedef struct {
84 struct user_object obj;
85 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
86 WORD Width; /* Width of the whole menu */
87 WORD Height; /* Height of the whole menu */
88 UINT nItems; /* Number of items in the menu */
89 HWND hWnd; /* Window containing the menu */
90 MENUITEM *items; /* Array of menu items */
91 UINT FocusedItem; /* Currently focused item */
92 HWND hwndOwner; /* window receiving the messages for ownerdraw */
93 BOOL bScrolling; /* Scroll arrows are active */
94 UINT nScrollPos; /* Current scroll position */
95 UINT nTotalHeight; /* Total height of menu items inside menu */
96 RECT items_rect; /* Rectangle within which the items lie. Excludes margins and scroll arrows */
97 LONG refcount;
98 /* ------------ MENUINFO members ------ */
99 DWORD dwStyle; /* Extended menu style */
100 UINT cyMax; /* max height of the whole menu, 0 is screen height */
101 HBRUSH hbrBack; /* brush for menu background */
102 DWORD dwContextHelpID;
103 ULONG_PTR dwMenuData; /* application defined value */
104 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
105 WORD textOffset; /* Offset of text when items have both bitmaps and text */
106 } POPUPMENU, *LPPOPUPMENU;
108 /* internal flags for menu tracking */
110 #define TF_ENDMENU 0x10000
111 #define TF_SUSPENDPOPUP 0x20000
112 #define TF_SKIPREMOVE 0x40000
113 #define TF_RCVD_BTN_UP 0x80000
115 typedef struct
117 UINT trackFlags;
118 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
119 HMENU hTopMenu; /* initial menu */
120 HWND hOwnerWnd; /* where notifications are sent */
121 POINT pt;
122 } MTRACKER;
124 #define ITEM_PREV -1
125 #define ITEM_NEXT 1
127 /* Internal MENU_TrackMenu() flags */
128 #define TPM_INTERNAL 0xF0000000
129 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
130 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
132 /* Space between 2 columns */
133 #define MENU_COL_SPACE 4
135 /* Margins for popup menus */
136 #define MENU_MARGIN 3
138 /* maximum allowed depth of any branch in the menu tree.
139 * This value is slightly larger than in windows (25) to
140 * stay on the safe side. */
141 #define MAXMENUDEPTH 30
143 /* (other menu->FocusedItem values give the position of the focused item) */
144 #define NO_SELECTED_ITEM 0xffff
146 #define MENU_ITEM_TYPE(flags) \
147 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
149 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
150 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
151 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
153 #define IS_SYSTEM_MENU(menu) \
154 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
156 #define MENUITEMINFO_TYPE_MASK \
157 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
158 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
159 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
160 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
161 #define STATE_MASK (~TYPE_MASK)
162 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
164 static SIZE menucharsize;
165 static UINT ODitemheight; /* default owner drawn item height */
167 /* Use global popup window because there's no way 2 menus can
168 * be tracked at the same time. */
169 static HWND top_popup;
170 static HMENU top_popup_hmenu;
172 /* Flag set by EndMenu() to force an exit from menu tracking */
173 static BOOL fEndMenu = FALSE;
175 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
177 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
179 static BOOL is_win_menu_disallowed(HWND hwnd)
181 return (GetWindowLongW(hwnd, GWL_STYLE) & (WS_CHILD | WS_POPUP)) == WS_CHILD;
184 /*********************************************************************
185 * menu class descriptor
187 const struct builtin_class_descr MENU_builtin_class =
189 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
190 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
191 WINPROC_MENU, /* proc */
192 sizeof(HMENU), /* extra */
193 IDC_ARROW, /* cursor */
194 (HBRUSH)(COLOR_MENU+1) /* brush */
198 /***********************************************************************
199 * debug_print_menuitem
201 * Print a menuitem in readable form.
204 #define debug_print_menuitem(pre, mp, post) \
205 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
207 #define MENUOUT(text) \
208 TRACE("%s%s", (count++ ? "," : ""), (text))
210 #define MENUFLAG(bit,text) \
211 do { \
212 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
213 } while (0)
215 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
216 const char *postfix)
218 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
219 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
220 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
221 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
222 TRACE("%s ", prefix);
223 if (mp) {
224 UINT flags = mp->fType;
225 TRACE( "{ ID=0x%lx", mp->wID);
226 if ( mp->hSubMenu)
227 TRACE( ", Sub=%p", mp->hSubMenu);
228 if (flags) {
229 int count = 0;
230 TRACE( ", fType=");
231 MENUFLAG( MFT_SEPARATOR, "sep");
232 MENUFLAG( MFT_OWNERDRAW, "own");
233 MENUFLAG( MFT_BITMAP, "bit");
234 MENUFLAG(MF_POPUP, "pop");
235 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
236 MENUFLAG(MFT_MENUBREAK, "brk");
237 MENUFLAG(MFT_RADIOCHECK, "radio");
238 MENUFLAG(MFT_RIGHTORDER, "rorder");
239 MENUFLAG(MF_SYSMENU, "sys");
240 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
241 if (flags)
242 TRACE( "+0x%x", flags);
244 flags = mp->fState;
245 if (flags) {
246 int count = 0;
247 TRACE( ", State=");
248 MENUFLAG(MFS_GRAYED, "grey");
249 MENUFLAG(MFS_DEFAULT, "default");
250 MENUFLAG(MFS_DISABLED, "dis");
251 MENUFLAG(MFS_CHECKED, "check");
252 MENUFLAG(MFS_HILITE, "hi");
253 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
254 MENUFLAG(MF_MOUSESELECT, "mouse");
255 if (flags)
256 TRACE( "+0x%x", flags);
258 if (mp->hCheckBit)
259 TRACE( ", Chk=%p", mp->hCheckBit);
260 if (mp->hUnCheckBit)
261 TRACE( ", Unc=%p", mp->hUnCheckBit);
262 if (mp->text)
263 TRACE( ", Text=%s", debugstr_w(mp->text));
264 if (mp->dwItemData)
265 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
266 if (mp->hbmpItem)
268 if( IS_MAGIC_BITMAP(mp->hbmpItem))
269 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
270 else
271 TRACE( ", hbitmap=%p", mp->hbmpItem);
273 TRACE( " }");
274 } else
275 TRACE( "NULL");
276 TRACE(" %s\n", postfix);
279 #undef MENUOUT
280 #undef MENUFLAG
283 /***********************************************************************
284 * MENU_GetMenu
286 * Validate the given menu handle and returns the menu structure pointer.
288 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
290 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
292 if (menu == OBJ_OTHER_PROCESS)
294 WARN( "other process menu %p?\n", hMenu);
295 return NULL;
297 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
298 else WARN("invalid menu handle=%p\n", hMenu);
299 return menu;
302 static POPUPMENU *grab_menu_ptr(HMENU hMenu)
304 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
306 if (menu == OBJ_OTHER_PROCESS)
308 WARN("other process menu %p?\n", hMenu);
309 return NULL;
312 if (menu)
313 menu->refcount++;
314 else
315 WARN("invalid menu handle=%p\n", hMenu);
316 return menu;
319 static void release_menu_ptr(POPUPMENU *menu)
321 if (menu)
323 menu->refcount--;
324 release_user_handle_ptr(menu);
328 /***********************************************************************
329 * get_win_sys_menu
331 * Get the system menu of a window
333 static HMENU get_win_sys_menu( HWND hwnd )
335 HMENU ret = 0;
336 WND *win = WIN_GetPtr( hwnd );
337 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
339 ret = win->hSysMenu;
340 WIN_ReleasePtr( win );
342 return ret;
345 /***********************************************************************
346 * get_menu_font
348 static HFONT get_menu_font( BOOL bold )
350 static HFONT hMenuFont, hMenuFontBold;
352 HFONT ret = bold ? hMenuFontBold : hMenuFont;
354 if (!ret)
356 NONCLIENTMETRICSW ncm;
357 HFONT prev;
359 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
360 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
362 if (bold)
364 ncm.lfMenuFont.lfWeight += 300;
365 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
367 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
368 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
369 ret, NULL );
370 if (prev)
372 /* another thread beat us to it */
373 DeleteObject( ret );
374 ret = prev;
377 return ret;
380 /***********************************************************************
381 * get_arrow_bitmap
383 static HBITMAP get_arrow_bitmap(void)
385 static HBITMAP arrow_bitmap;
387 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
388 return arrow_bitmap;
391 static inline UINT get_scroll_arrow_height(const POPUPMENU *menu)
393 return menucharsize.cy + 4;
396 /***********************************************************************
397 * MENU_CopySysPopup
399 * Return the default system menu.
401 static HMENU MENU_CopySysPopup(BOOL mdi)
403 HMENU hMenu = LoadMenuW(user32_module, mdi ? L"SYSMENUMDI" : L"SYSMENU");
405 if( hMenu ) {
406 MENUINFO minfo;
407 MENUITEMINFOW miteminfo;
408 POPUPMENU* menu = MENU_GetMenu(hMenu);
409 menu->wFlags |= MF_SYSMENU | MF_POPUP;
410 /* decorate the menu with bitmaps */
411 minfo.cbSize = sizeof( MENUINFO);
412 minfo.dwStyle = MNS_CHECKORBMP;
413 minfo.fMask = MIM_STYLE;
414 SetMenuInfo( hMenu, &minfo);
415 miteminfo.cbSize = sizeof( MENUITEMINFOW);
416 miteminfo.fMask = MIIM_BITMAP;
417 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
418 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
419 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
420 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
421 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
422 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
423 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
424 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
425 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
427 else
428 ERR("Unable to load default system menu\n" );
430 TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
432 return hMenu;
436 /**********************************************************************
437 * MENU_GetSysMenu
439 * Create a copy of the system menu. System menu in Windows is
440 * a special menu bar with the single entry - system menu popup.
441 * This popup is presented to the outside world as a "system menu".
442 * However, the real system menu handle is sometimes seen in the
443 * WM_MENUSELECT parameters (and Word 6 likes it this way).
445 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
447 HMENU hMenu;
449 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
450 if ((hMenu = CreateMenu()))
452 POPUPMENU *menu = MENU_GetMenu(hMenu);
453 menu->wFlags = MF_SYSMENU;
454 menu->hWnd = WIN_GetFullHandle( hWnd );
455 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
457 if (!hPopupMenu)
459 if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
460 hPopupMenu = MENU_CopySysPopup(TRUE);
461 else
462 hPopupMenu = MENU_CopySysPopup(FALSE);
465 if (hPopupMenu)
467 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
468 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
470 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
471 (UINT_PTR)hPopupMenu, NULL );
473 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
474 menu->items[0].fState = 0;
475 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
477 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
478 return hMenu;
480 DestroyMenu( hMenu );
482 ERR("failed to load system menu!\n");
483 return 0;
487 /***********************************************************************
488 * MENU_InitSysMenuPopup
490 * Grey the appropriate items in System menu.
492 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
494 BOOL gray;
496 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
497 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
498 gray = ((style & WS_MAXIMIZE) != 0);
499 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
500 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
501 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
502 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
503 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
504 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
505 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
506 gray = (clsStyle & CS_NOCLOSE) != 0;
508 /* The menu item must keep its state if it's disabled */
509 if(gray)
510 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
514 /******************************************************************************
516 * UINT MENU_GetStartOfNextColumn(
517 * HMENU hMenu )
519 *****************************************************************************/
521 static UINT MENU_GetStartOfNextColumn(
522 HMENU hMenu )
524 POPUPMENU *menu = MENU_GetMenu(hMenu);
525 UINT i;
527 if(!menu)
528 return NO_SELECTED_ITEM;
530 i = menu->FocusedItem + 1;
531 if( i == NO_SELECTED_ITEM )
532 return i;
534 for( ; i < menu->nItems; ++i ) {
535 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
536 return i;
539 return NO_SELECTED_ITEM;
543 /******************************************************************************
545 * UINT MENU_GetStartOfPrevColumn(
546 * HMENU hMenu )
548 *****************************************************************************/
550 static UINT MENU_GetStartOfPrevColumn(
551 HMENU hMenu )
553 POPUPMENU *menu = MENU_GetMenu(hMenu);
554 UINT i;
556 if( !menu )
557 return NO_SELECTED_ITEM;
559 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
560 return NO_SELECTED_ITEM;
562 /* Find the start of the column */
564 for(i = menu->FocusedItem; i != 0 &&
565 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
566 --i); /* empty */
568 if(i == 0)
569 return NO_SELECTED_ITEM;
571 for(--i; i != 0; --i) {
572 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
573 break;
576 TRACE("ret %d.\n", i );
578 return i;
581 static POPUPMENU *find_menu_item(HMENU hmenu, UINT id, UINT flags, UINT *pos)
583 UINT fallback_pos = ~0u, i;
584 POPUPMENU *menu;
586 menu = grab_menu_ptr(hmenu);
587 if (!menu)
588 return NULL;
590 if (flags & MF_BYPOSITION)
592 if (id >= menu->nItems)
594 release_menu_ptr(menu);
595 return NULL;
598 if (pos) *pos = id;
599 return menu;
601 else
603 MENUITEM *item = menu->items;
604 for (i = 0; i < menu->nItems; i++, item++)
606 if (item->fType & MF_POPUP)
608 POPUPMENU *submenu = find_menu_item(item->hSubMenu, id, flags, pos);
610 if (submenu)
612 release_menu_ptr(menu);
613 return submenu;
615 else if (item->wID == id)
617 /* fallback to this item if nothing else found */
618 fallback_pos = i;
621 else if (item->wID == id)
623 if (pos) *pos = i;
624 return menu;
629 if (fallback_pos != ~0u)
630 *pos = fallback_pos;
631 else
633 release_menu_ptr(menu);
634 menu = NULL;
637 return menu;
640 /***********************************************************************
641 * MENU_FindSubMenu
643 * Find a Sub menu. Return the position of the submenu, and modifies
644 * *hmenu in case it is found in another sub-menu.
645 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
647 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
649 POPUPMENU *menu;
650 UINT i;
651 MENUITEM *item;
652 if (((*hmenu)==(HMENU)0xffff) ||
653 (!(menu = MENU_GetMenu(*hmenu))))
654 return NO_SELECTED_ITEM;
655 item = menu->items;
656 for (i = 0; i < menu->nItems; i++, item++) {
657 if(!(item->fType & MF_POPUP)) continue;
658 if (item->hSubMenu == hSubTarget) {
659 return i;
661 else {
662 HMENU hsubmenu = item->hSubMenu;
663 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
664 if (pos != NO_SELECTED_ITEM) {
665 *hmenu = hsubmenu;
666 return pos;
670 return NO_SELECTED_ITEM;
673 /***********************************************************************
674 * MENU_FreeItemData
676 static void MENU_FreeItemData( MENUITEM* item )
678 /* delete text */
679 HeapFree( GetProcessHeap(), 0, item->text );
682 /***********************************************************************
683 * MENU_AdjustMenuItemRect
685 * Adjust menu item rectangle according to scrolling state.
687 static void
688 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
690 INT scroll_offset = menu->bScrolling ? menu->nScrollPos : 0;
692 OffsetRect( rect, menu->items_rect.left, menu->items_rect.top - scroll_offset );
695 enum hittest
697 ht_nowhere, /* outside the menu */
698 ht_border, /* anywhere that's not an item or a scroll arrow */
699 ht_item, /* a menu item */
700 ht_scroll_up, /* scroll up arrow */
701 ht_scroll_down /* scroll down arrow */
704 /***********************************************************************
705 * MENU_FindItemByCoords
707 * Find the item at the specified coordinates (screen coords). Does
708 * not work for child windows and therefore should not be called for
709 * an arbitrary system menu.
711 * Returns a hittest code. *pos will contain the position of the
712 * item or NO_SELECTED_ITEM. If the hittest code is ht_scroll_up
713 * or ht_scroll_down then *pos will contain the position of the
714 * item that's just outside the items_rect - ie, the one that would
715 * be scrolled completely into view.
717 static enum hittest MENU_FindItemByCoords( const POPUPMENU *menu, POINT pt, UINT *pos )
719 MENUITEM *item;
720 UINT i;
721 RECT rect;
722 enum hittest ht = ht_border;
724 *pos = NO_SELECTED_ITEM;
726 if (!GetWindowRect(menu->hWnd, &rect) || !PtInRect(&rect, pt))
727 return ht_nowhere;
729 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
730 else pt.x -= rect.left;
731 pt.y -= rect.top;
733 if (!PtInRect(&menu->items_rect, pt))
735 if (!menu->bScrolling || pt.x < menu->items_rect.left || pt.x >= menu->items_rect.right)
736 return ht_border;
738 /* On a scroll arrow. Update pt so that it points to the item just outside items_rect */
739 if (pt.y < menu->items_rect.top)
741 ht = ht_scroll_up;
742 pt.y = menu->items_rect.top - 1;
744 else
746 ht = ht_scroll_down;
747 pt.y = menu->items_rect.bottom;
751 item = menu->items;
752 for (i = 0; i < menu->nItems; i++, item++)
754 rect = item->rect;
755 MENU_AdjustMenuItemRect(menu, &rect);
756 if (PtInRect(&rect, pt))
758 *pos = i;
759 if (ht != ht_scroll_up && ht != ht_scroll_down) ht = ht_item;
760 break;
764 return ht;
768 /***********************************************************************
769 * MENU_FindItemByKey
771 * Find the menu item selected by a key press.
772 * Return item id, -1 if none, -2 if we should close the menu.
774 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
775 WCHAR key, BOOL forceMenuChar )
777 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
779 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
781 if (hmenu)
783 POPUPMENU *menu = MENU_GetMenu( hmenu );
784 MENUITEM *item = menu->items;
785 LRESULT menuchar;
787 if( !forceMenuChar )
789 UINT i;
790 BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
792 for (i = 0; i < menu->nItems; i++, item++)
794 if( item->text)
796 const WCHAR *p = item->text - 2;
799 const WCHAR *q = p + 2;
800 p = wcschr (q, '&');
801 if (!p && cjk) p = wcschr (q, '\036'); /* Japanese Win16 */
803 while (p != NULL && p [1] == '&');
804 if (p && (towupper(p[1]) == towupper(key))) return i;
808 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
809 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
810 if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
811 if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
813 return (UINT)(-1);
817 /***********************************************************************
818 * MENU_GetBitmapItemSize
820 * Get the size of a bitmap item.
822 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
823 HWND hwndOwner)
825 BITMAP bm;
826 HBITMAP bmp = lpitem->hbmpItem;
828 size->cx = size->cy = 0;
830 /* check if there is a magic menu item associated with this item */
831 switch( (INT_PTR) bmp )
833 case (INT_PTR)HBMMENU_CALLBACK:
835 MEASUREITEMSTRUCT measItem;
836 measItem.CtlType = ODT_MENU;
837 measItem.CtlID = 0;
838 measItem.itemID = lpitem->wID;
839 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
840 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
841 measItem.itemData = lpitem->dwItemData;
842 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
843 size->cx = measItem.itemWidth;
844 size->cy = measItem.itemHeight;
845 return;
847 break;
848 case (INT_PTR)HBMMENU_SYSTEM:
849 if (lpitem->dwItemData)
851 bmp = (HBITMAP)lpitem->dwItemData;
852 break;
854 /* fall through */
855 case (INT_PTR)HBMMENU_MBAR_RESTORE:
856 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
857 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
858 case (INT_PTR)HBMMENU_MBAR_CLOSE:
859 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
860 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
861 size->cy = size->cx;
862 return;
863 case (INT_PTR)HBMMENU_POPUP_CLOSE:
864 case (INT_PTR)HBMMENU_POPUP_RESTORE:
865 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
866 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
867 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
868 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
869 return;
871 if (GetObjectW(bmp, sizeof(bm), &bm ))
873 size->cx = bm.bmWidth;
874 size->cy = bm.bmHeight;
878 /***********************************************************************
879 * MENU_DrawBitmapItem
881 * Draw a bitmap item.
883 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
884 POPUPMENU *menu, HWND hwndOwner, UINT odaction )
886 BITMAP bm;
887 DWORD rop;
888 HDC hdcMem;
889 HBITMAP bmp;
890 int w = rect->right - rect->left;
891 int h = rect->bottom - rect->top;
892 int bmp_xoffset = 0;
893 int left, top;
894 HBITMAP hbmToDraw = lpitem->hbmpItem;
895 bmp = hbmToDraw;
897 /* Check if there is a magic menu item associated with this item */
898 if (IS_MAGIC_BITMAP(hbmToDraw))
900 UINT flags = 0;
901 WCHAR bmchr = 0;
902 RECT r;
904 switch((INT_PTR)hbmToDraw)
906 case (INT_PTR)HBMMENU_SYSTEM:
907 if (lpitem->dwItemData)
909 bmp = (HBITMAP)lpitem->dwItemData;
910 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
912 else
914 static HBITMAP hBmpSysMenu;
916 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
917 bmp = hBmpSysMenu;
918 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
919 /* only use right half of the bitmap */
920 bmp_xoffset = bm.bmWidth / 2;
921 bm.bmWidth -= bmp_xoffset;
923 goto got_bitmap;
924 case (INT_PTR)HBMMENU_MBAR_RESTORE:
925 flags = DFCS_CAPTIONRESTORE;
926 break;
927 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
928 flags = DFCS_CAPTIONMIN;
929 break;
930 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
931 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
932 break;
933 case (INT_PTR)HBMMENU_MBAR_CLOSE:
934 flags = DFCS_CAPTIONCLOSE;
935 break;
936 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
937 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
938 break;
939 case (INT_PTR)HBMMENU_CALLBACK:
941 DRAWITEMSTRUCT drawItem;
942 drawItem.CtlType = ODT_MENU;
943 drawItem.CtlID = 0;
944 drawItem.itemID = lpitem->wID;
945 drawItem.itemAction = odaction;
946 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
947 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
948 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
949 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
950 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
951 drawItem.hwndItem = (HWND)menu->obj.handle;
952 drawItem.hDC = hdc;
953 drawItem.itemData = lpitem->dwItemData;
954 drawItem.rcItem = *rect;
955 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
956 return;
958 break;
959 case (INT_PTR)HBMMENU_POPUP_CLOSE:
960 bmchr = 0x72;
961 break;
962 case (INT_PTR)HBMMENU_POPUP_RESTORE:
963 bmchr = 0x32;
964 break;
965 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
966 bmchr = 0x31;
967 break;
968 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
969 bmchr = 0x30;
970 break;
971 default:
972 FIXME("Magic %p not implemented\n", hbmToDraw);
973 return;
975 if (bmchr)
977 /* draw the magic bitmaps using marlett font characters */
978 /* FIXME: fontsize and the position (x,y) could probably be better */
979 HFONT hfont, hfontsav;
980 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, L"Marlett" };
981 logfont.lfHeight = min( h, w) - 5 ;
982 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
983 hfont = CreateFontIndirectW( &logfont);
984 hfontsav = SelectObject(hdc, hfont);
985 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
986 SelectObject(hdc, hfontsav);
987 DeleteObject( hfont);
989 else
991 r = *rect;
992 InflateRect( &r, -1, -1 );
993 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
994 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
996 return;
999 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
1001 got_bitmap:
1002 hdcMem = CreateCompatibleDC( hdc );
1003 SelectObject( hdcMem, bmp );
1005 /* handle fontsize > bitmap_height */
1006 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
1007 left=rect->left;
1008 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
1009 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
1010 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1011 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
1012 DeleteDC( hdcMem );
1016 /***********************************************************************
1017 * MENU_CalcItemSize
1019 * Calculate the size of the menu item and store it in lpitem->rect.
1021 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1022 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1024 WCHAR *p;
1025 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1026 UINT arrow_bitmap_width;
1027 BITMAP bm;
1028 INT itemheight;
1030 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1031 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1032 (menuBar ? " (MenuBar)" : ""));
1034 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1035 arrow_bitmap_width = bm.bmWidth;
1037 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1038 if( !menucharsize.cx ) {
1039 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1040 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1041 * but it is unlikely an application will depend on that */
1042 ODitemheight = HIWORD( GetDialogBaseUnits());
1045 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1047 if (lpitem->fType & MF_OWNERDRAW)
1049 MEASUREITEMSTRUCT mis;
1050 mis.CtlType = ODT_MENU;
1051 mis.CtlID = 0;
1052 mis.itemID = lpitem->wID;
1053 mis.itemData = lpitem->dwItemData;
1054 mis.itemHeight = ODitemheight;
1055 mis.itemWidth = 0;
1056 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1057 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1058 * width of a menufont character to the width of an owner-drawn menu.
1060 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1061 if (menuBar) {
1062 /* under at least win95 you seem to be given a standard
1063 height for the menu and the height value is ignored */
1064 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1065 } else
1066 lpitem->rect.bottom += mis.itemHeight;
1068 TRACE("id=%04lx size=%dx%d\n",
1069 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1070 lpitem->rect.bottom-lpitem->rect.top);
1071 return;
1074 if (lpitem->fType & MF_SEPARATOR)
1076 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1077 if( !menuBar)
1078 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1079 return;
1082 itemheight = 0;
1083 lpitem->xTab = 0;
1085 if (!menuBar) {
1086 if (lpitem->hbmpItem) {
1087 SIZE size;
1089 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1090 /* Keep the size of the bitmap in callback mode to be able
1091 * to draw it correctly */
1092 lpitem->bmpsize = size;
1093 lppop->textOffset = max( lppop->textOffset, size.cx);
1094 lpitem->rect.right += size.cx + 2;
1095 itemheight = size.cy + 2;
1097 if( !(lppop->dwStyle & MNS_NOCHECK))
1098 lpitem->rect.right += check_bitmap_width;
1099 lpitem->rect.right += 4 + menucharsize.cx;
1100 lpitem->xTab = lpitem->rect.right;
1101 lpitem->rect.right += arrow_bitmap_width;
1102 } else if (lpitem->hbmpItem) { /* menuBar */
1103 SIZE size;
1105 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1106 lpitem->bmpsize = size;
1107 lpitem->rect.right += size.cx;
1108 if( lpitem->text) lpitem->rect.right += 2;
1109 itemheight = size.cy;
1112 /* it must be a text item - unless it's the system menu */
1113 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1114 HFONT hfontOld = NULL;
1115 RECT rc = lpitem->rect;
1116 LONG txtheight, txtwidth;
1118 if ( lpitem->fState & MFS_DEFAULT ) {
1119 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1121 if (menuBar) {
1122 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1123 DT_SINGLELINE|DT_CALCRECT);
1124 lpitem->rect.right += rc.right - rc.left;
1125 itemheight = max( max( itemheight, txtheight),
1126 GetSystemMetrics( SM_CYMENU) - 1);
1127 lpitem->rect.right += 2 * menucharsize.cx;
1128 } else {
1129 if ((p = wcschr( lpitem->text, '\t' )) != NULL) {
1130 RECT tmprc = rc;
1131 LONG tmpheight;
1132 int n = (int)( p - lpitem->text);
1133 /* Item contains a tab (only meaningful in popup menus) */
1134 /* get text size before the tab */
1135 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1136 DT_SINGLELINE|DT_CALCRECT);
1137 txtwidth = rc.right - rc.left;
1138 p += 1; /* advance past the Tab */
1139 /* get text size after the tab */
1140 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1141 DT_SINGLELINE|DT_CALCRECT);
1142 lpitem->xTab += txtwidth;
1143 txtheight = max( txtheight, tmpheight);
1144 txtwidth += menucharsize.cx + /* space for the tab */
1145 tmprc.right - tmprc.left; /* space for the short cut */
1146 } else {
1147 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1148 DT_SINGLELINE|DT_CALCRECT);
1149 txtwidth = rc.right - rc.left;
1150 lpitem->xTab += txtwidth;
1152 lpitem->rect.right += 2 + txtwidth;
1153 itemheight = max( itemheight,
1154 max( txtheight + 2, menucharsize.cy + 4));
1156 if (hfontOld) SelectObject (hdc, hfontOld);
1157 } else if( menuBar) {
1158 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1160 lpitem->rect.bottom += itemheight;
1161 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1164 /***********************************************************************
1165 * MENU_PopupMenuCalcSize
1167 * Calculate the size of a popup menu.
1169 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, UINT max_height )
1171 MENUITEM *lpitem;
1172 HDC hdc;
1173 UINT start, i;
1174 BOOL textandbmp = FALSE, multi_col = FALSE;
1175 int orgX, orgY, maxTab, maxTabWidth;
1177 lppop->Width = lppop->Height = 0;
1178 SetRectEmpty(&lppop->items_rect);
1180 if (lppop->nItems == 0) return;
1181 hdc = GetDC( 0 );
1183 SelectObject( hdc, get_menu_font(FALSE));
1185 start = 0;
1187 lppop->textOffset = 0;
1189 while (start < lppop->nItems)
1191 lpitem = &lppop->items[start];
1192 orgX = lppop->items_rect.right;
1193 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1194 orgX += MENU_COL_SPACE;
1195 orgY = lppop->items_rect.top;
1197 maxTab = maxTabWidth = 0;
1198 /* Parse items until column break or end of menu */
1199 for (i = start; i < lppop->nItems; i++, lpitem++)
1201 if (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1203 multi_col = TRUE;
1204 if (i != start) break;
1207 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1208 lppop->items_rect.right = max( lppop->items_rect.right, lpitem->rect.right );
1209 orgY = lpitem->rect.bottom;
1210 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1212 maxTab = max( maxTab, lpitem->xTab );
1213 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1215 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1218 /* Finish the column (set all items to the largest width found) */
1219 lppop->items_rect.right = max( lppop->items_rect.right, maxTab + maxTabWidth );
1220 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1222 lpitem->rect.right = lppop->items_rect.right;
1223 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1224 lpitem->xTab = maxTab;
1227 lppop->items_rect.bottom = max( lppop->items_rect.bottom, orgY );
1230 /* if none of the items have both text and bitmap then
1231 * the text and bitmaps are all aligned on the left. If there is at
1232 * least one item with both text and bitmap then bitmaps are
1233 * on the left and texts left aligned with the right hand side
1234 * of the bitmaps */
1235 if( !textandbmp) lppop->textOffset = 0;
1237 lppop->nTotalHeight = lppop->items_rect.bottom;
1239 /* space for the border */
1240 OffsetRect(&lppop->items_rect, MENU_MARGIN, MENU_MARGIN);
1241 lppop->Height = lppop->items_rect.bottom + MENU_MARGIN;
1242 lppop->Width = lppop->items_rect.right + MENU_MARGIN;
1244 /* Adjust popup height if it exceeds maximum */
1245 if (lppop->Height >= max_height)
1247 lppop->Height = max_height;
1248 lppop->bScrolling = !multi_col;
1249 /* When the scroll arrows are present, don't add the top/bottom margin as well */
1250 if (lppop->bScrolling)
1252 lppop->items_rect.top = get_scroll_arrow_height(lppop);
1253 lppop->items_rect.bottom = lppop->Height - get_scroll_arrow_height(lppop);
1256 else
1258 lppop->bScrolling = FALSE;
1261 ReleaseDC( 0, hdc );
1265 /***********************************************************************
1266 * MENU_MenuBarCalcSize
1268 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1269 * height is off by 1 pixel which causes lengthy window relocations when
1270 * active document window is maximized/restored.
1272 * Calculate the size of the menu bar.
1274 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1275 LPPOPUPMENU lppop, HWND hwndOwner )
1277 MENUITEM *lpitem;
1278 UINT start, i, helpPos;
1279 int orgX, orgY;
1281 if ((lprect == NULL) || (lppop == NULL)) return;
1282 if (lppop->nItems == 0) return;
1283 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1284 /* Start with a 1 pixel top border.
1285 This corresponds to the difference between SM_CYMENU and SM_CYMENUSIZE. */
1286 SetRect(&lppop->items_rect, 0, 0, lprect->right - lprect->left, 1);
1287 start = 0;
1288 helpPos = ~0U;
1289 lppop->textOffset = 0;
1290 while (start < lppop->nItems)
1292 lpitem = &lppop->items[start];
1293 orgX = lppop->items_rect.left;
1294 orgY = lppop->items_rect.bottom;
1296 /* Parse items until line break or end of menu */
1297 for (i = start; i < lppop->nItems; i++, lpitem++)
1299 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1300 if ((i != start) &&
1301 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1303 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1304 debug_print_menuitem (" item: ", lpitem, "");
1305 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1307 if (lpitem->rect.right > lppop->items_rect.right)
1309 if (i != start) break;
1310 else lpitem->rect.right = lppop->items_rect.right;
1312 lppop->items_rect.bottom = max( lppop->items_rect.bottom, lpitem->rect.bottom );
1313 orgX = lpitem->rect.right;
1316 /* Finish the line (set all items to the largest height found) */
1317 while (start < i) lppop->items[start++].rect.bottom = lppop->items_rect.bottom;
1320 OffsetRect(&lppop->items_rect, lprect->left, lprect->top);
1321 lppop->Width = lppop->items_rect.right - lppop->items_rect.left;
1322 lppop->Height = lppop->items_rect.bottom - lppop->items_rect.top;
1323 lprect->bottom = lppop->items_rect.bottom;
1325 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1326 /* the last item (if several lines, only move the last line) */
1327 if (helpPos == ~0U) return;
1328 lpitem = &lppop->items[lppop->nItems-1];
1329 orgY = lpitem->rect.top;
1330 orgX = lprect->right - lprect->left;
1331 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1332 if (lpitem->rect.top != orgY) break; /* Other line */
1333 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1334 lpitem->rect.left += orgX - lpitem->rect.right;
1335 lpitem->rect.right = orgX;
1336 orgX = lpitem->rect.left;
1340 static void draw_scroll_arrow(HDC hdc, int x, int top, int height, BOOL up, BOOL enabled)
1342 RECT rect, light_rect;
1343 HBRUSH brush = GetSysColorBrush( enabled ? COLOR_BTNTEXT : COLOR_BTNSHADOW );
1344 HBRUSH light = GetSysColorBrush( COLOR_3DLIGHT );
1346 if (!up)
1348 top = top + height;
1349 if (!enabled)
1351 SetRect( &rect, x + 1, top, x + 2, top + 1);
1352 FillRect( hdc, &rect, light );
1354 top--;
1357 SetRect( &rect, x, top, x + 1, top + 1);
1358 while (height--)
1360 FillRect( hdc, &rect, brush );
1361 if (!enabled && !up && height)
1363 SetRect( &light_rect, rect.right, rect.top, rect.right + 2, rect.bottom );
1364 FillRect( hdc, &light_rect, light );
1366 InflateRect( &rect, 1, 0 );
1367 OffsetRect( &rect, 0, up ? 1 : -1 );
1370 if (!enabled && up)
1372 rect.left += 2;
1373 FillRect( hdc, &rect, light );
1377 /***********************************************************************
1378 * MENU_DrawScrollArrows
1380 * Draw scroll arrows.
1382 static void
1383 MENU_DrawScrollArrows(const POPUPMENU *menu, HDC hdc)
1385 UINT full_height = get_scroll_arrow_height( menu );
1386 UINT arrow_height = full_height / 3;
1387 BOOL at_end = menu->nScrollPos + menu->items_rect.bottom - menu->items_rect.top == menu->nTotalHeight;
1389 draw_scroll_arrow( hdc, menu->Width / 3, arrow_height, arrow_height,
1390 TRUE, menu->nScrollPos != 0);
1391 draw_scroll_arrow( hdc, menu->Width / 3, menu->Height - 2 * arrow_height, arrow_height,
1392 FALSE, !at_end );
1396 /***********************************************************************
1397 * draw_popup_arrow
1399 * Draws the popup-menu arrow.
1401 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1402 UINT arrow_bitmap_height)
1404 HDC hdcMem = CreateCompatibleDC( hdc );
1405 HBITMAP hOrigBitmap;
1407 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1408 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1409 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1410 arrow_bitmap_width, arrow_bitmap_height,
1411 hdcMem, 0, 0, SRCCOPY );
1412 SelectObject( hdcMem, hOrigBitmap );
1413 DeleteDC( hdcMem );
1415 /***********************************************************************
1416 * MENU_DrawMenuItem
1418 * Draw a single menu item.
1420 static void MENU_DrawMenuItem( HWND hwnd, POPUPMENU *menu, HWND hwndOwner, HDC hdc,
1421 MENUITEM *lpitem, BOOL menuBar, UINT odaction )
1423 RECT rect, bmprc;
1424 BOOL flat_menu = FALSE;
1425 int bkgnd;
1426 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1427 HRGN old_clip = NULL, clip;
1429 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1431 if (!menuBar) {
1432 BITMAP bmp;
1433 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1434 arrow_bitmap_width = bmp.bmWidth;
1435 arrow_bitmap_height = bmp.bmHeight;
1438 if (lpitem->fType & MF_SYSMENU)
1440 if( !IsIconic(hwnd) )
1441 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1442 return;
1445 TRACE( "rect=%s\n", wine_dbgstr_rect( &lpitem->rect ) );
1446 rect = lpitem->rect;
1447 MENU_AdjustMenuItemRect( menu, &rect );
1448 if (!IntersectRect( &bmprc, &rect, &menu->items_rect )) /* bmprc is used as a dummy */
1449 return;
1451 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1452 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1454 /* Setup colors */
1456 if (lpitem->fState & MF_HILITE)
1458 if(menuBar && !flat_menu) {
1459 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1460 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1461 } else {
1462 if(lpitem->fState & MF_GRAYED)
1463 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1464 else
1465 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1466 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1469 else
1471 if (lpitem->fState & MF_GRAYED)
1472 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1473 else
1474 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1475 SetBkColor( hdc, GetSysColor( bkgnd ) );
1478 old_clip = CreateRectRgn( 0, 0, 0, 0 );
1479 if (GetClipRgn( hdc, old_clip ) <= 0)
1481 DeleteObject( old_clip );
1482 old_clip = NULL;
1484 clip = CreateRectRgnIndirect( &menu->items_rect );
1485 ExtSelectClipRgn( hdc, clip, RGN_AND );
1486 DeleteObject( clip );
1488 if (lpitem->fType & MF_OWNERDRAW)
1491 ** Experimentation under Windows reveals that an owner-drawn
1492 ** menu is given the rectangle which includes the space it requested
1493 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1494 ** and a popup-menu arrow. This is the value of lpitem->rect.
1495 ** Windows will leave all drawing to the application except for
1496 ** the popup-menu arrow. Windows always draws that itself, after
1497 ** the menu owner has finished drawing.
1499 DRAWITEMSTRUCT dis;
1500 COLORREF old_bk, old_text;
1502 dis.CtlType = ODT_MENU;
1503 dis.CtlID = 0;
1504 dis.itemID = lpitem->wID;
1505 dis.itemData = lpitem->dwItemData;
1506 dis.itemState = 0;
1507 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1508 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1509 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1510 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1511 dis.hwndItem = (HWND)menu->obj.handle;
1512 dis.hDC = hdc;
1513 dis.rcItem = rect;
1514 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1515 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1516 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1517 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1518 old_bk = GetBkColor( hdc );
1519 old_text = GetTextColor( hdc );
1520 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1521 /* Draw the popup-menu arrow */
1522 SetBkColor( hdc, old_bk );
1523 SetTextColor( hdc, old_text );
1524 if (lpitem->fType & MF_POPUP)
1525 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1526 arrow_bitmap_height);
1527 goto done;
1530 if (menuBar && (lpitem->fType & MF_SEPARATOR)) goto done;
1532 if (lpitem->fState & MF_HILITE)
1534 if (flat_menu)
1536 InflateRect (&rect, -1, -1);
1537 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1538 InflateRect (&rect, 1, 1);
1539 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1541 else
1543 if(menuBar)
1544 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1545 else
1546 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1549 else
1550 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1552 SetBkMode( hdc, TRANSPARENT );
1554 /* vertical separator */
1555 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1557 HPEN oldPen;
1558 RECT rc = rect;
1560 rc.left -= MENU_COL_SPACE / 2 + 1;
1561 rc.top = 3;
1562 rc.bottom = menu->Height - 3;
1563 if (flat_menu)
1565 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1566 MoveToEx( hdc, rc.left, rc.top, NULL );
1567 LineTo( hdc, rc.left, rc.bottom );
1568 SelectObject( hdc, oldPen );
1570 else
1571 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1574 /* horizontal separator */
1575 if (lpitem->fType & MF_SEPARATOR)
1577 HPEN oldPen;
1578 RECT rc = rect;
1580 InflateRect( &rc, -1, 0 );
1581 rc.top = ( rc.top + rc.bottom) / 2;
1582 if (flat_menu)
1584 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1585 MoveToEx( hdc, rc.left, rc.top, NULL );
1586 LineTo( hdc, rc.right, rc.top );
1587 SelectObject( hdc, oldPen );
1589 else
1590 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1591 goto done;
1594 /* helper lines for debugging */
1595 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1596 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1597 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1598 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1601 if (lpitem->hbmpItem) {
1602 /* calculate the bitmap rectangle in coordinates relative
1603 * to the item rectangle */
1604 if( menuBar) {
1605 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1606 bmprc.left = 3;
1607 else
1608 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1610 else if (menu->dwStyle & MNS_NOCHECK)
1611 bmprc.left = 4;
1612 else if (menu->dwStyle & MNS_CHECKORBMP)
1613 bmprc.left = 2;
1614 else
1615 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1616 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1617 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1618 bmprc.top = 0;
1619 else
1620 bmprc.top = (rect.bottom - rect.top -
1621 lpitem->bmpsize.cy) / 2;
1622 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1625 if (!menuBar)
1627 HBITMAP bm;
1628 INT y = rect.top + rect.bottom;
1629 BOOL checked = FALSE;
1630 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1631 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1632 /* Draw the check mark
1634 * FIXME:
1635 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1637 if (!(menu->dwStyle & MNS_NOCHECK))
1639 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1640 lpitem->hUnCheckBit;
1641 if (bm) /* we have a custom bitmap */
1643 HDC hdcMem = CreateCompatibleDC( hdc );
1645 SelectObject( hdcMem, bm );
1646 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1647 check_bitmap_width, check_bitmap_height,
1648 hdcMem, 0, 0, SRCCOPY );
1649 DeleteDC( hdcMem );
1650 checked = TRUE;
1652 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1654 RECT r;
1655 HBITMAP bm = CreateBitmap( check_bitmap_width,
1656 check_bitmap_height, 1, 1, NULL );
1657 HDC hdcMem = CreateCompatibleDC( hdc );
1659 SelectObject( hdcMem, bm );
1660 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1661 DrawFrameControl( hdcMem, &r, DFC_MENU,
1662 (lpitem->fType & MFT_RADIOCHECK) ?
1663 DFCS_MENUBULLET : DFCS_MENUCHECK );
1664 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1665 hdcMem, 0, 0, SRCCOPY );
1666 DeleteDC( hdcMem );
1667 DeleteObject( bm );
1668 checked = TRUE;
1671 if (lpitem->hbmpItem && !(checked && (menu->dwStyle & MNS_CHECKORBMP)))
1673 POINT origorg;
1674 /* some applications make this assumption on the DC's origin */
1675 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1676 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, menu, hwndOwner, odaction );
1677 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1679 /* Draw the popup-menu arrow */
1680 if (lpitem->fType & MF_POPUP)
1681 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1682 arrow_bitmap_height);
1683 rect.left += 4;
1684 if( !(menu->dwStyle & MNS_NOCHECK))
1685 rect.left += check_bitmap_width;
1686 rect.right -= arrow_bitmap_width;
1688 else if (lpitem->hbmpItem)
1689 { /* Draw the bitmap */
1690 POINT origorg;
1692 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1693 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, menu, hwndOwner, odaction );
1694 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1696 /* process text if present */
1697 if (lpitem->text)
1699 int i;
1700 HFONT hfontOld = 0;
1702 UINT uFormat = (menuBar) ?
1703 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1704 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1706 if( !(menu->dwStyle & MNS_CHECKORBMP))
1707 rect.left += menu->textOffset;
1709 if ( lpitem->fState & MFS_DEFAULT )
1711 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1714 if (menuBar) {
1715 if( lpitem->hbmpItem)
1716 rect.left += lpitem->bmpsize.cx;
1717 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1718 rect.left += menucharsize.cx;
1719 rect.right -= menucharsize.cx;
1722 for (i = 0; lpitem->text[i]; i++)
1723 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1724 break;
1726 if(lpitem->fState & MF_GRAYED)
1728 if (!(lpitem->fState & MF_HILITE) )
1730 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1731 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1732 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1733 --rect.left; --rect.top; --rect.right; --rect.bottom;
1735 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1738 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1740 /* paint the shortcut text */
1741 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1743 if (lpitem->text[i] == '\t')
1745 rect.left = lpitem->xTab;
1746 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1748 else
1750 rect.right = lpitem->xTab;
1751 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1754 if(lpitem->fState & MF_GRAYED)
1756 if (!(lpitem->fState & MF_HILITE) )
1758 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1759 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1760 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1761 --rect.left; --rect.top; --rect.right; --rect.bottom;
1763 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1765 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1768 if (hfontOld)
1769 SelectObject (hdc, hfontOld);
1772 done:
1773 ExtSelectClipRgn( hdc, old_clip, RGN_COPY );
1774 if (old_clip) DeleteObject( old_clip );
1778 /***********************************************************************
1779 * MENU_DrawPopupMenu
1781 * Paint a popup menu.
1783 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1785 HBRUSH hPrevBrush, brush = GetSysColorBrush( COLOR_MENU );
1786 RECT rect;
1787 POPUPMENU *menu = MENU_GetMenu( hmenu );
1789 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1791 GetClientRect( hwnd, &rect );
1793 if (menu && menu->hbrBack) brush = menu->hbrBack;
1794 if ((hPrevBrush = SelectObject( hdc, brush ))
1795 && SelectObject( hdc, get_menu_font(FALSE) ))
1797 HPEN hPrevPen;
1799 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1801 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1802 if( hPrevPen )
1804 BOOL flat_menu = FALSE;
1806 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1807 if (flat_menu)
1808 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1809 else
1810 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1812 if (menu)
1814 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1815 /* draw menu items */
1816 if (menu->nItems)
1818 MENUITEM *item;
1819 UINT u;
1821 item = menu->items;
1822 for (u = menu->nItems; u > 0; u--, item++)
1823 MENU_DrawMenuItem( hwnd, menu, menu->hwndOwner, hdc,
1824 item, FALSE, ODA_DRAWENTIRE );
1826 /* draw scroll arrows */
1827 if (menu->bScrolling)
1828 MENU_DrawScrollArrows(menu, hdc);
1830 } else
1832 SelectObject( hdc, hPrevBrush );
1837 /***********************************************************************
1838 * MENU_DrawMenuBar
1840 * Paint a menu bar. Returns the height of the menu bar.
1841 * called from [windows/nonclient.c]
1843 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd )
1845 LPPOPUPMENU lppop;
1846 HMENU hMenu = GetMenu(hwnd);
1848 lppop = MENU_GetMenu( hMenu );
1849 if (lppop == NULL || lprect == NULL)
1851 return GetSystemMetrics(SM_CYMENU);
1854 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1858 /***********************************************************************
1859 * MENU_InitPopup
1861 * Popup menu initialization before WM_ENTERMENULOOP.
1863 static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
1865 POPUPMENU *menu;
1866 DWORD ex_style = 0;
1868 TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
1870 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1872 /* store the owner for DrawItem */
1873 if (!IsWindow( hwndOwner ))
1875 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1876 return FALSE;
1878 menu->hwndOwner = hwndOwner;
1880 if (flags & TPM_LAYOUTRTL)
1881 ex_style = WS_EX_LAYOUTRTL;
1883 /* NOTE: In Windows, top menu popup is not owned. */
1884 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1885 WS_POPUP, 0, 0, 0, 0,
1886 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1887 (LPVOID)hmenu );
1888 if( !menu->hWnd ) return FALSE;
1889 return TRUE;
1893 /***********************************************************************
1894 * MENU_ShowPopup
1896 * Display a popup menu.
1898 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1899 INT x, INT y, INT xanchor, INT yanchor )
1901 POPUPMENU *menu;
1902 POINT pt;
1903 HMONITOR monitor;
1904 MONITORINFO info;
1905 UINT max_height;
1907 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1908 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1910 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1911 if (menu->FocusedItem != NO_SELECTED_ITEM)
1913 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1914 menu->FocusedItem = NO_SELECTED_ITEM;
1917 menu->nScrollPos = 0;
1919 /* FIXME: should use item rect */
1920 pt.x = x;
1921 pt.y = y;
1922 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1923 info.cbSize = sizeof(info);
1924 GetMonitorInfoW( monitor, &info );
1926 max_height = info.rcWork.bottom - info.rcWork.top;
1927 if (menu->cyMax)
1928 max_height = min( max_height, menu->cyMax );
1930 MENU_PopupMenuCalcSize( menu, max_height );
1932 /* adjust popup menu pos so that it fits within the desktop */
1934 if (flags & TPM_LAYOUTRTL)
1935 flags ^= TPM_RIGHTALIGN;
1937 if( flags & TPM_RIGHTALIGN ) x -= menu->Width;
1938 if( flags & TPM_CENTERALIGN ) x -= menu->Width / 2;
1940 if( flags & TPM_BOTTOMALIGN ) y -= menu->Height;
1941 if( flags & TPM_VCENTERALIGN ) y -= menu->Height / 2;
1943 if( x + menu->Width > info.rcWork.right)
1945 if( xanchor && x >= menu->Width - xanchor )
1946 x -= menu->Width - xanchor;
1948 if( x + menu->Width > info.rcWork.right)
1949 x = info.rcWork.right - menu->Width;
1951 if( x < info.rcWork.left ) x = info.rcWork.left;
1953 if( y + menu->Height > info.rcWork.bottom)
1955 if( yanchor && y >= menu->Height + yanchor )
1956 y -= menu->Height + yanchor;
1958 if( y + menu->Height > info.rcWork.bottom)
1959 y = info.rcWork.bottom - menu->Height;
1961 if( y < info.rcWork.top ) y = info.rcWork.top;
1963 if (!top_popup) {
1964 top_popup = menu->hWnd;
1965 top_popup_hmenu = hmenu;
1967 /* Display the window */
1969 SetWindowPos( menu->hWnd, HWND_TOPMOST, x, y, menu->Width, menu->Height,
1970 SWP_SHOWWINDOW | SWP_NOACTIVATE );
1971 UpdateWindow( menu->hWnd );
1972 return TRUE;
1976 /***********************************************************************
1977 * MENU_EnsureMenuItemVisible
1979 static void
1980 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1982 if (lppop->bScrolling)
1984 MENUITEM *item = &lppop->items[wIndex];
1985 UINT nOldPos = lppop->nScrollPos;
1986 const RECT *rc = &lppop->items_rect;
1987 UINT scroll_height = rc->bottom - rc->top;
1989 if (item->rect.bottom > lppop->nScrollPos + scroll_height)
1991 lppop->nScrollPos = item->rect.bottom - scroll_height;
1992 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, rc, rc);
1994 else if (item->rect.top < lppop->nScrollPos)
1996 lppop->nScrollPos = item->rect.top;
1997 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, rc, rc);
2000 /* Invalidate the scroll arrows if necessary */
2001 if (nOldPos != lppop->nScrollPos)
2003 RECT arrow_rect = lppop->items_rect;
2004 if (nOldPos == 0 || lppop->nScrollPos == 0)
2006 arrow_rect.top = 0;
2007 arrow_rect.bottom = lppop->items_rect.top;
2008 InvalidateRect(lppop->hWnd, &arrow_rect, FALSE);
2010 if (nOldPos + scroll_height == lppop->nTotalHeight ||
2011 lppop->nScrollPos + scroll_height == lppop->nTotalHeight)
2013 arrow_rect.top = lppop->items_rect.bottom;
2014 arrow_rect.bottom = lppop->Height;
2015 InvalidateRect(lppop->hWnd, &arrow_rect, FALSE);
2022 /***********************************************************************
2023 * MENU_SelectItem
2025 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
2026 BOOL sendMenuSelect, HMENU topmenu )
2028 LPPOPUPMENU lppop;
2029 HDC hdc;
2031 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
2033 lppop = MENU_GetMenu( hmenu );
2034 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
2036 if (lppop->FocusedItem == wIndex) return;
2037 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
2038 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2039 if (!top_popup) {
2040 top_popup = lppop->hWnd;
2041 top_popup_hmenu = hmenu;
2044 SelectObject( hdc, get_menu_font(FALSE));
2046 /* Clear previous highlighted item */
2047 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2049 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2050 MENU_DrawMenuItem( lppop->hWnd, lppop, hwndOwner, hdc, &lppop->items[lppop->FocusedItem],
2051 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2054 /* Highlight new item (if any) */
2055 lppop->FocusedItem = wIndex;
2056 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2058 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2059 lppop->items[wIndex].fState |= MF_HILITE;
2060 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2061 MENU_DrawMenuItem( lppop->hWnd, lppop, hwndOwner, hdc, &lppop->items[wIndex],
2062 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2064 if (sendMenuSelect)
2066 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2067 SendMessageW( hwndOwner, WM_MENUSELECT,
2068 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2069 ip->fType | ip->fState |
2070 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2073 else if (sendMenuSelect) {
2074 if(topmenu){
2075 int pos;
2076 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2077 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2078 MENUITEM *ip = &ptm->items[pos];
2079 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2080 ip->fType | ip->fState |
2081 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2085 ReleaseDC( lppop->hWnd, hdc );
2089 /***********************************************************************
2090 * MENU_MoveSelection
2092 * Moves currently selected item according to the offset parameter.
2093 * If there is no selection then it should select the last item if
2094 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2096 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2098 INT i;
2099 POPUPMENU *menu;
2101 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2103 menu = MENU_GetMenu( hmenu );
2104 if ((!menu) || (!menu->items)) return;
2106 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2108 if( menu->nItems == 1 ) return; else
2109 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2110 ; i += offset)
2111 if (!(menu->items[i].fType & MF_SEPARATOR))
2113 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2114 return;
2118 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2119 i >= 0 && i < menu->nItems ; i += offset)
2120 if (!(menu->items[i].fType & MF_SEPARATOR))
2122 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2123 return;
2128 /**********************************************************************
2129 * insert_menu_item
2131 * Insert (allocate) a new item into a menu.
2133 static POPUPMENU *insert_menu_item(HMENU hMenu, UINT id, UINT flags, UINT *ret_pos)
2135 MENUITEM *newItems;
2136 POPUPMENU *menu;
2137 UINT pos = id;
2139 /* Find where to insert new item */
2140 if (!(menu = find_menu_item(hMenu, id, flags, &pos)))
2142 if (!(menu = grab_menu_ptr(hMenu)))
2143 return NULL;
2144 pos = menu->nItems;
2147 /* Make sure that MDI system buttons stay on the right side.
2148 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2149 * regardless of their id.
2151 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2152 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2153 pos--;
2155 TRACE("inserting at %u flags %x\n", pos, flags);
2157 /* Create new items array */
2159 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2160 if (!newItems)
2162 release_menu_ptr(menu);
2163 WARN("allocation failed\n" );
2164 return NULL;
2166 if (menu->nItems > 0)
2168 /* Copy the old array into the new one */
2169 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2170 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2171 (menu->nItems-pos)*sizeof(MENUITEM) );
2172 HeapFree( GetProcessHeap(), 0, menu->items );
2174 menu->items = newItems;
2175 menu->nItems++;
2176 memset( &newItems[pos], 0, sizeof(*newItems) );
2177 menu->Height = 0; /* force size recalculate */
2179 *ret_pos = pos;
2180 return menu;
2184 /**********************************************************************
2185 * MENU_ParseResource
2187 * Parse a standard menu resource and add items to the menu.
2188 * Return a pointer to the end of the resource.
2190 * NOTE: flags is equivalent to the mtOption field
2192 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2194 WORD flags, id = 0;
2195 LPCWSTR str;
2196 BOOL end_flag;
2200 flags = GET_WORD(res);
2201 end_flag = flags & MF_END;
2202 /* Remove MF_END because it has the same value as MF_HILITE */
2203 flags &= ~MF_END;
2204 res += sizeof(WORD);
2205 if (!(flags & MF_POPUP))
2207 id = GET_WORD(res);
2208 res += sizeof(WORD);
2210 str = (LPCWSTR)res;
2211 res += (lstrlenW(str) + 1) * sizeof(WCHAR);
2212 if (flags & MF_POPUP)
2214 HMENU hSubMenu = CreatePopupMenu();
2215 if (!hSubMenu) return NULL;
2216 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2217 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2219 else /* Not a popup */
2221 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2223 } while (!end_flag);
2224 return res;
2228 /**********************************************************************
2229 * MENUEX_ParseResource
2231 * Parse an extended menu resource and add items to the menu.
2232 * Return a pointer to the end of the resource.
2234 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2236 WORD resinfo;
2237 do {
2238 MENUITEMINFOW mii;
2240 mii.cbSize = sizeof(mii);
2241 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2242 mii.fType = GET_DWORD(res);
2243 res += sizeof(DWORD);
2244 mii.fState = GET_DWORD(res);
2245 res += sizeof(DWORD);
2246 mii.wID = GET_DWORD(res);
2247 res += sizeof(DWORD);
2248 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2249 res += sizeof(WORD);
2250 /* Align the text on a word boundary. */
2251 res += (~((UINT_PTR)res - 1)) & 1;
2252 mii.dwTypeData = (LPWSTR) res;
2253 res += (1 + lstrlenW(mii.dwTypeData)) * sizeof(WCHAR);
2254 /* Align the following fields on a dword boundary. */
2255 res += (~((UINT_PTR)res - 1)) & 3;
2257 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2258 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2260 if (resinfo & 1) { /* Pop-up? */
2261 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2262 res += sizeof(DWORD);
2263 mii.hSubMenu = CreatePopupMenu();
2264 if (!mii.hSubMenu)
2265 return NULL;
2266 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2267 DestroyMenu(mii.hSubMenu);
2268 return NULL;
2270 mii.fMask |= MIIM_SUBMENU;
2271 mii.fType |= MF_POPUP;
2273 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2275 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2276 mii.wID, mii.fType);
2277 mii.fType |= MF_SEPARATOR;
2279 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2280 } while (!(resinfo & MF_END));
2281 return res;
2285 /***********************************************************************
2286 * MENU_GetSubPopup
2288 * Return the handle of the selected sub-popup menu (if any).
2290 static HMENU MENU_GetSubPopup( HMENU hmenu )
2292 POPUPMENU *menu;
2293 MENUITEM *item;
2295 menu = MENU_GetMenu( hmenu );
2297 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2299 item = &menu->items[menu->FocusedItem];
2300 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2301 return item->hSubMenu;
2302 return 0;
2306 /***********************************************************************
2307 * MENU_HideSubPopups
2309 * Hide the sub-popup menus of this menu.
2311 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2312 BOOL sendMenuSelect, UINT wFlags )
2314 POPUPMENU *menu = MENU_GetMenu( hmenu );
2316 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2318 if (menu && top_popup)
2320 HMENU hsubmenu;
2321 POPUPMENU *submenu;
2322 MENUITEM *item;
2324 if (menu->FocusedItem != NO_SELECTED_ITEM)
2326 item = &menu->items[menu->FocusedItem];
2327 if (!(item->fType & MF_POPUP) ||
2328 !(item->fState & MF_MOUSESELECT)) return;
2329 item->fState &= ~MF_MOUSESELECT;
2330 hsubmenu = item->hSubMenu;
2331 } else return;
2333 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2334 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2335 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2336 DestroyWindow( submenu->hWnd );
2337 submenu->hWnd = 0;
2339 if (!(wFlags & TPM_NONOTIFY))
2340 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2341 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2346 /***********************************************************************
2347 * MENU_ShowSubPopup
2349 * Display the sub-menu of the selected item of this menu.
2350 * Return the handle of the submenu, or hmenu if no submenu to display.
2352 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2353 BOOL selectFirst, UINT wFlags )
2355 RECT rect;
2356 POPUPMENU *menu;
2357 MENUITEM *item;
2358 HDC hdc;
2360 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2362 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2364 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2366 item = &menu->items[menu->FocusedItem];
2367 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2368 return hmenu;
2370 /* message must be sent before using item,
2371 because nearly everything may be changed by the application ! */
2373 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2374 if (!(wFlags & TPM_NONOTIFY))
2375 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2376 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2378 item = &menu->items[menu->FocusedItem];
2379 rect = item->rect;
2381 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2382 if (!(item->fState & MF_HILITE))
2384 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2385 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2387 SelectObject( hdc, get_menu_font(FALSE));
2389 item->fState |= MF_HILITE;
2390 MENU_DrawMenuItem( menu->hWnd, menu, hwndOwner, hdc, item, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2391 ReleaseDC( menu->hWnd, hdc );
2393 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2394 item->rect = rect;
2396 item->fState |= MF_MOUSESELECT;
2398 if (IS_SYSTEM_MENU(menu))
2400 MENU_InitSysMenuPopup(item->hSubMenu,
2401 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2402 GetClassLongW( menu->hWnd, GCL_STYLE));
2404 NC_GetSysPopupPos( menu->hWnd, &rect );
2405 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2406 rect.top = rect.bottom;
2407 rect.right = GetSystemMetrics(SM_CXSIZE);
2408 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2410 else
2412 RECT item_rect = item->rect;
2414 MENU_AdjustMenuItemRect(menu, &item_rect);
2415 GetWindowRect( menu->hWnd, &rect );
2417 if (menu->wFlags & MF_POPUP)
2419 /* The first item in the popup menu has to be at the
2420 same y position as the focused menu item */
2421 if (wFlags & TPM_LAYOUTRTL)
2422 rect.left += GetSystemMetrics(SM_CXBORDER);
2423 else
2424 rect.left += item_rect.right - GetSystemMetrics(SM_CXBORDER);
2425 rect.top += item_rect.top - MENU_MARGIN;
2426 rect.right = item_rect.left - item_rect.right + GetSystemMetrics(SM_CXBORDER);
2427 rect.bottom = item_rect.top - item_rect.bottom - 2 * MENU_MARGIN;
2429 else
2431 if (wFlags & TPM_LAYOUTRTL)
2432 rect.left = rect.right - item_rect.left;
2433 else
2434 rect.left += item_rect.left;
2435 rect.top += item_rect.bottom;
2436 rect.right = item_rect.right - item_rect.left;
2437 rect.bottom = item_rect.bottom - item_rect.top;
2441 /* use default alignment for submenus */
2442 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2444 MENU_InitPopup( hwndOwner, item->hSubMenu, wFlags );
2446 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2447 rect.left, rect.top, rect.right, rect.bottom );
2448 if (selectFirst)
2449 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2450 return item->hSubMenu;
2455 /**********************************************************************
2456 * MENU_IsMenuActive
2458 HWND MENU_IsMenuActive(void)
2460 return top_popup;
2463 /**********************************************************************
2464 * MENU_EndMenu
2466 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2468 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2470 void MENU_EndMenu( HWND hwnd )
2472 POPUPMENU *menu;
2473 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2474 if (menu && (hwnd == menu->hWnd || hwnd == menu->hwndOwner)) EndMenu();
2477 /***********************************************************************
2478 * MENU_PtMenu
2480 * Walks menu chain trying to find a menu pt maps to.
2482 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2484 POPUPMENU *menu = MENU_GetMenu( hMenu );
2485 UINT item = menu->FocusedItem;
2486 HMENU ret;
2488 /* try subpopup first (if any) */
2489 ret = (item != NO_SELECTED_ITEM &&
2490 (menu->items[item].fType & MF_POPUP) &&
2491 (menu->items[item].fState & MF_MOUSESELECT))
2492 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2494 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2496 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2497 if( menu->wFlags & MF_POPUP )
2499 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2501 else if (ht == HTSYSMENU)
2502 ret = get_win_sys_menu( menu->hWnd );
2503 else if (ht == HTMENU)
2504 ret = GetMenu( menu->hWnd );
2506 return ret;
2509 /***********************************************************************
2510 * MENU_ExecFocusedItem
2512 * Execute a menu item (for instance when user pressed Enter).
2513 * Return the wID of the executed item. Otherwise, -1 indicating
2514 * that no menu item was executed, -2 if a popup is shown;
2515 * Have to receive the flags for the TrackPopupMenu options to avoid
2516 * sending unwanted message.
2519 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2521 MENUITEM *item;
2522 POPUPMENU *menu = MENU_GetMenu( hMenu );
2524 TRACE("%p hmenu=%p\n", pmt, hMenu);
2526 if (!menu || !menu->nItems ||
2527 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2529 item = &menu->items[menu->FocusedItem];
2531 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2533 if (!(item->fType & MF_POPUP))
2535 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2537 /* If TPM_RETURNCMD is set you return the id, but
2538 do not send a message to the owner */
2539 if(!(wFlags & TPM_RETURNCMD))
2541 if( menu->wFlags & MF_SYSMENU )
2542 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2543 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2544 else
2546 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2547 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2549 if (dwStyle & MNS_NOTIFYBYPOS)
2550 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2551 (LPARAM)hMenu);
2552 else
2553 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2556 return item->wID;
2559 else
2561 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2562 return -2;
2565 return -1;
2568 /***********************************************************************
2569 * MENU_SwitchTracking
2571 * Helper function for menu navigation routines.
2573 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2575 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2576 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2578 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2580 if( pmt->hTopMenu != hPtMenu &&
2581 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2583 /* both are top level menus (system and menu-bar) */
2584 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2585 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2586 pmt->hTopMenu = hPtMenu;
2588 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2589 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2593 /***********************************************************************
2594 * MENU_ButtonDown
2596 * Return TRUE if we can go on with menu tracking.
2598 static BOOL MENU_ButtonDown( MTRACKER* pmt, UINT message, HMENU hPtMenu, UINT wFlags )
2600 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2602 if (hPtMenu)
2604 UINT pos;
2605 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2606 enum hittest ht = ht_item;
2608 if( IS_SYSTEM_MENU(ptmenu) )
2610 if (message == WM_LBUTTONDBLCLK) return FALSE;
2611 pos = 0;
2613 else
2614 ht = MENU_FindItemByCoords( ptmenu, pmt->pt, &pos );
2616 if (pos != NO_SELECTED_ITEM)
2618 if (ptmenu->FocusedItem != pos)
2619 MENU_SwitchTracking( pmt, hPtMenu, pos, wFlags );
2621 /* If the popup menu is not already "popped" */
2622 if (!(ptmenu->items[pos].fState & MF_MOUSESELECT))
2623 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2626 /* A click on an item or anywhere on a popup keeps tracking going */
2627 if (ht == ht_item || ((ptmenu->wFlags & MF_POPUP) && ht != ht_nowhere))
2628 return TRUE;
2630 return FALSE;
2633 /***********************************************************************
2634 * MENU_ButtonUp
2636 * Return the value of MENU_ExecFocusedItem if
2637 * the selected item was not a popup. Else open the popup.
2638 * A -1 return value indicates that we go on with menu tracking.
2641 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2643 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2645 if (hPtMenu)
2647 UINT pos;
2648 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2650 if( IS_SYSTEM_MENU(ptmenu) )
2651 pos = 0;
2652 else if (MENU_FindItemByCoords( ptmenu, pmt->pt, &pos ) != ht_item)
2653 pos = NO_SELECTED_ITEM;
2655 if (pos != NO_SELECTED_ITEM && (ptmenu->FocusedItem == pos))
2657 debug_print_menuitem ("FocusedItem: ", &ptmenu->items[pos], "");
2659 if (!(ptmenu->items[pos].fType & MF_POPUP))
2661 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2662 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2663 return executedMenuId;
2666 /* If we are dealing with the menu bar */
2667 /* and this is a click on an already "popped" item: */
2668 /* Stop the menu tracking and close the opened submenus */
2669 if(((pmt->hTopMenu == hPtMenu) || IS_SYSTEM_MENU(ptmenu)) && (pmt->trackFlags & TF_RCVD_BTN_UP))
2670 return 0;
2672 if( GetMenu(ptmenu->hWnd) == hPtMenu || IS_SYSTEM_MENU(ptmenu) )
2674 if (pos == NO_SELECTED_ITEM) return 0;
2675 pmt->trackFlags |= TF_RCVD_BTN_UP;
2678 return -1;
2682 /***********************************************************************
2683 * MENU_MouseMove
2685 * Return TRUE if we can go on with menu tracking.
2687 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2689 UINT id = NO_SELECTED_ITEM;
2690 POPUPMENU *ptmenu = NULL;
2692 if( hPtMenu )
2694 ptmenu = MENU_GetMenu( hPtMenu );
2695 if( IS_SYSTEM_MENU(ptmenu) )
2696 id = 0;
2697 else if (MENU_FindItemByCoords( ptmenu, pmt->pt, &id ) != ht_item)
2698 id = NO_SELECTED_ITEM;
2701 if( id == NO_SELECTED_ITEM )
2703 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2704 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2707 else if( ptmenu->FocusedItem != id )
2709 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2710 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2712 return TRUE;
2716 /***********************************************************************
2717 * MENU_DoNextMenu
2719 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2721 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2723 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2724 BOOL atEnd = FALSE;
2726 /* When skipping left, we need to do something special after the
2727 first menu. */
2728 if (vk == VK_LEFT && menu->FocusedItem == 0)
2730 atEnd = TRUE;
2732 /* When skipping right, for the non-system menu, we need to
2733 handle the last non-special menu item (ie skip any window
2734 icons such as MDI maximize, restore or close) */
2735 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2737 UINT i = menu->FocusedItem + 1;
2738 while (i < menu->nItems) {
2739 if ((menu->items[i].wID >= SC_SIZE &&
2740 menu->items[i].wID <= SC_RESTORE)) {
2741 i++;
2742 } else break;
2744 if (i == menu->nItems) {
2745 atEnd = TRUE;
2748 /* When skipping right, we need to cater for the system menu */
2749 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2751 if (menu->FocusedItem == (menu->nItems - 1)) {
2752 atEnd = TRUE;
2756 if( atEnd )
2758 MDINEXTMENU next_menu;
2759 HMENU hNewMenu;
2760 HWND hNewWnd;
2761 UINT id = 0;
2763 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2764 next_menu.hmenuNext = 0;
2765 next_menu.hwndNext = 0;
2766 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2768 TRACE("%p [%p] -> %p [%p]\n",
2769 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2771 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2773 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2774 hNewWnd = pmt->hOwnerWnd;
2775 if( IS_SYSTEM_MENU(menu) )
2777 /* switch to the menu bar */
2779 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2781 if( vk == VK_LEFT )
2783 menu = MENU_GetMenu( hNewMenu );
2784 id = menu->nItems - 1;
2786 /* Skip backwards over any system predefined icons,
2787 eg. MDI close, restore etc icons */
2788 while ((id > 0) &&
2789 (menu->items[id].wID >= SC_SIZE &&
2790 menu->items[id].wID <= SC_RESTORE)) id--;
2793 else if (style & WS_SYSMENU )
2795 /* switch to the system menu */
2796 hNewMenu = get_win_sys_menu( hNewWnd );
2798 else return FALSE;
2800 else /* application returned a new menu to switch to */
2802 hNewMenu = next_menu.hmenuNext;
2803 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2805 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2807 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2809 if (style & WS_SYSMENU &&
2810 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2812 /* get the real system menu */
2813 hNewMenu = get_win_sys_menu(hNewWnd);
2815 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2817 /* FIXME: Not sure what to do here;
2818 * perhaps try to track hNewMenu as a popup? */
2820 TRACE(" -- got confused.\n");
2821 return FALSE;
2824 else return FALSE;
2827 if( hNewMenu != pmt->hTopMenu )
2829 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2830 FALSE, 0 );
2831 if( pmt->hCurrentMenu != pmt->hTopMenu )
2832 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2835 if( hNewWnd != pmt->hOwnerWnd )
2837 pmt->hOwnerWnd = hNewWnd;
2838 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2841 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2842 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2844 return TRUE;
2846 return FALSE;
2849 /***********************************************************************
2850 * MENU_SuspendPopup
2852 * The idea is not to show the popup if the next input message is
2853 * going to hide it anyway.
2855 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT uMsg )
2857 MSG msg;
2859 msg.hwnd = pmt->hOwnerWnd;
2861 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2862 pmt->trackFlags |= TF_SKIPREMOVE;
2864 switch( uMsg )
2866 case WM_KEYDOWN:
2867 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2868 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2870 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2871 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2872 if( msg.message == WM_KEYDOWN &&
2873 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2875 pmt->trackFlags |= TF_SUSPENDPOPUP;
2876 return TRUE;
2879 break;
2882 /* failures go through this */
2883 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2884 return FALSE;
2887 /***********************************************************************
2888 * MENU_KeyEscape
2890 * Handle a VK_ESCAPE key event in a menu.
2892 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2894 BOOL bEndMenu = TRUE;
2896 if (pmt->hCurrentMenu != pmt->hTopMenu)
2898 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2900 if (menu->wFlags & MF_POPUP)
2902 HMENU hmenutmp, hmenuprev;
2904 hmenuprev = hmenutmp = pmt->hTopMenu;
2906 /* close topmost popup */
2907 while (hmenutmp != pmt->hCurrentMenu)
2909 hmenuprev = hmenutmp;
2910 hmenutmp = MENU_GetSubPopup( hmenuprev );
2913 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2914 pmt->hCurrentMenu = hmenuprev;
2915 bEndMenu = FALSE;
2919 return bEndMenu;
2922 /***********************************************************************
2923 * MENU_KeyLeft
2925 * Handle a VK_LEFT key event in a menu.
2927 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags, UINT msg )
2929 POPUPMENU *menu;
2930 HMENU hmenutmp, hmenuprev;
2931 UINT prevcol;
2933 hmenuprev = hmenutmp = pmt->hTopMenu;
2934 menu = MENU_GetMenu( hmenutmp );
2936 /* Try to move 1 column left (if possible) */
2937 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2938 NO_SELECTED_ITEM ) {
2940 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2941 prevcol, TRUE, 0 );
2942 return;
2945 /* close topmost popup */
2946 while (hmenutmp != pmt->hCurrentMenu)
2948 hmenuprev = hmenutmp;
2949 hmenutmp = MENU_GetSubPopup( hmenuprev );
2952 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2953 pmt->hCurrentMenu = hmenuprev;
2955 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2957 /* move menu bar selection if no more popups are left */
2959 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2960 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2962 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2964 /* A sublevel menu was displayed - display the next one
2965 * unless there is another displacement coming up */
2967 if( !MENU_SuspendPopup( pmt, msg ) )
2968 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2969 pmt->hTopMenu, TRUE, wFlags);
2975 /***********************************************************************
2976 * MENU_KeyRight
2978 * Handle a VK_RIGHT key event in a menu.
2980 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags, UINT msg )
2982 HMENU hmenutmp;
2983 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2984 UINT nextcol;
2986 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2987 pmt->hCurrentMenu,
2988 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2989 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2991 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2993 /* If already displaying a popup, try to display sub-popup */
2995 hmenutmp = pmt->hCurrentMenu;
2996 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2998 /* if subpopup was displayed then we are done */
2999 if (hmenutmp != pmt->hCurrentMenu) return;
3002 /* Check to see if there's another column */
3003 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
3004 NO_SELECTED_ITEM ) {
3005 TRACE("Going to %d.\n", nextcol );
3006 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
3007 nextcol, TRUE, 0 );
3008 return;
3011 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
3013 if( pmt->hCurrentMenu != pmt->hTopMenu )
3015 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
3016 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
3017 } else hmenutmp = 0;
3019 /* try to move to the next item */
3020 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
3021 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
3023 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
3024 if( !MENU_SuspendPopup( pmt, msg ) )
3025 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
3026 pmt->hTopMenu, TRUE, wFlags);
3030 static void CALLBACK release_capture( BOOL __normal )
3032 set_capture_window( 0, GUI_INMENUMODE, NULL );
3035 /***********************************************************************
3036 * MENU_TrackMenu
3038 * Menu tracking code.
3040 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
3041 HWND hwnd, const RECT *lprect )
3043 MSG msg;
3044 POPUPMENU *menu;
3045 BOOL fRemove;
3046 INT executedMenuId = -1;
3047 MTRACKER mt;
3048 BOOL enterIdleSent = FALSE;
3049 HWND capture_win;
3051 mt.trackFlags = 0;
3052 mt.hCurrentMenu = hmenu;
3053 mt.hTopMenu = hmenu;
3054 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3055 mt.pt.x = x;
3056 mt.pt.y = y;
3058 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3059 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3061 if (!(menu = MENU_GetMenu( hmenu )))
3063 WARN("Invalid menu handle %p\n", hmenu);
3064 SetLastError(ERROR_INVALID_MENU_HANDLE);
3065 return FALSE;
3068 if (wFlags & TPM_BUTTONDOWN)
3070 /* Get the result in order to start the tracking or not */
3071 fRemove = MENU_ButtonDown( &mt, WM_LBUTTONDOWN, hmenu, wFlags );
3072 fEndMenu = !fRemove;
3075 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3077 /* owner may not be visible when tracking a popup, so use the menu itself */
3078 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3079 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3081 if ((wFlags & TPM_POPUPMENU) && menu->nItems == 0)
3082 return FALSE;
3084 __TRY while (!fEndMenu)
3086 menu = MENU_GetMenu( mt.hCurrentMenu );
3087 if (!menu) /* sometimes happens if I do a window manager close */
3088 break;
3090 /* we have to keep the message in the queue until it's
3091 * clear that menu loop is not over yet. */
3093 for (;;)
3095 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3097 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3098 /* remove the message from the queue */
3099 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3101 else
3103 if (!enterIdleSent)
3105 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3106 enterIdleSent = TRUE;
3107 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3109 WaitMessage();
3113 /* check if EndMenu() tried to cancel us, by posting this message */
3114 if(msg.message == WM_CANCELMODE)
3116 /* we are now out of the loop */
3117 fEndMenu = TRUE;
3119 /* remove the message from the queue */
3120 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3122 /* break out of internal loop, ala ESCAPE */
3123 break;
3126 mt.pt = msg.pt;
3128 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3129 enterIdleSent=FALSE;
3131 fRemove = FALSE;
3132 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3135 * Use the mouse coordinates in lParam instead of those in the MSG
3136 * struct to properly handle synthetic messages. They are already
3137 * in screen coordinates.
3139 mt.pt.x = (short)LOWORD(msg.lParam);
3140 mt.pt.y = (short)HIWORD(msg.lParam);
3142 /* Find a menu for this mouse event */
3143 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3145 switch(msg.message)
3147 /* no WM_NC... messages in captured state */
3149 case WM_RBUTTONDBLCLK:
3150 case WM_RBUTTONDOWN:
3151 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3152 /* fall through */
3153 case WM_LBUTTONDBLCLK:
3154 case WM_LBUTTONDOWN:
3155 /* If the message belongs to the menu, removes it from the queue */
3156 /* Else, end menu tracking */
3157 fRemove = MENU_ButtonDown( &mt, msg.message, hmenu, wFlags );
3158 fEndMenu = !fRemove;
3159 break;
3161 case WM_RBUTTONUP:
3162 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3163 /* fall through */
3164 case WM_LBUTTONUP:
3165 /* Check if a menu was selected by the mouse */
3166 if (hmenu)
3168 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3169 TRACE("executedMenuId %d\n", executedMenuId);
3171 /* End the loop if executedMenuId is an item ID */
3172 /* or if the job was done (executedMenuId = 0). */
3173 fEndMenu = fRemove = (executedMenuId != -1);
3175 /* No menu was selected by the mouse */
3176 /* if the function was called by TrackPopupMenu, continue
3177 with the menu tracking. If not, stop it */
3178 else
3179 fEndMenu = !(wFlags & TPM_POPUPMENU);
3181 break;
3183 case WM_MOUSEMOVE:
3184 /* the selected menu item must be changed every time */
3185 /* the mouse moves. */
3187 if (hmenu)
3188 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3190 } /* switch(msg.message) - mouse */
3192 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3194 fRemove = TRUE; /* Keyboard messages are always removed */
3195 switch(msg.message)
3197 case WM_KEYDOWN:
3198 case WM_SYSKEYDOWN:
3199 switch(msg.wParam)
3201 case VK_MENU:
3202 case VK_F10:
3203 fEndMenu = TRUE;
3204 break;
3206 case VK_HOME:
3207 case VK_END:
3208 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3209 NO_SELECTED_ITEM, FALSE, 0 );
3210 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3211 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3212 break;
3214 case VK_UP:
3215 case VK_DOWN: /* If on menu bar, pull-down the menu */
3217 menu = MENU_GetMenu( mt.hCurrentMenu );
3218 if (!(menu->wFlags & MF_POPUP))
3219 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3220 else /* otherwise try to move selection */
3221 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3222 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3223 break;
3225 case VK_LEFT:
3226 MENU_KeyLeft( &mt, wFlags, msg.message );
3227 break;
3229 case VK_RIGHT:
3230 MENU_KeyRight( &mt, wFlags, msg.message );
3231 break;
3233 case VK_ESCAPE:
3234 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3235 break;
3237 case VK_F1:
3239 HELPINFO hi;
3240 hi.cbSize = sizeof(HELPINFO);
3241 hi.iContextType = HELPINFO_MENUITEM;
3242 if (menu->FocusedItem == NO_SELECTED_ITEM)
3243 hi.iCtrlId = 0;
3244 else
3245 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3246 hi.hItemHandle = hmenu;
3247 hi.dwContextId = menu->dwContextHelpID;
3248 hi.MousePos = msg.pt;
3249 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3250 break;
3253 default:
3254 TranslateMessage( &msg );
3255 break;
3257 break; /* WM_KEYDOWN */
3259 case WM_CHAR:
3260 case WM_SYSCHAR:
3262 UINT pos;
3264 if (msg.wParam == '\r' || msg.wParam == ' ')
3266 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3267 fEndMenu = (executedMenuId != -2);
3269 break;
3272 /* Hack to avoid control chars. */
3273 /* We will find a better way real soon... */
3274 if (msg.wParam < 32) break;
3276 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3277 LOWORD(msg.wParam), FALSE );
3278 if (pos == (UINT)-2) fEndMenu = TRUE;
3279 else if (pos == (UINT)-1) MessageBeep(0);
3280 else
3282 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3283 TRUE, 0 );
3284 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3285 fEndMenu = (executedMenuId != -2);
3288 break;
3289 } /* switch(msg.message) - kbd */
3291 else
3293 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3294 DispatchMessageW( &msg );
3295 continue;
3298 if (!fEndMenu) fRemove = TRUE;
3300 /* finally remove message from the queue */
3302 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3303 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3304 else mt.trackFlags &= ~TF_SKIPREMOVE;
3306 __FINALLY( release_capture )
3308 /* If dropdown is still painted and the close box is clicked on
3309 then the menu will be destroyed as part of the DispatchMessage above.
3310 This will then invalidate the menu handle in mt.hTopMenu. We should
3311 check for this first. */
3312 if( IsMenu( mt.hTopMenu ) )
3314 menu = MENU_GetMenu( mt.hTopMenu );
3316 if( IsWindow( mt.hOwnerWnd ) )
3318 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3320 if (menu && (menu->wFlags & MF_POPUP))
3322 DestroyWindow( menu->hWnd );
3323 menu->hWnd = 0;
3325 if (!(wFlags & TPM_NONOTIFY))
3326 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3327 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3329 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3330 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3334 SetLastError( ERROR_SUCCESS );
3335 /* The return value is only used by TrackPopupMenu */
3336 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3337 if (executedMenuId == -1) executedMenuId = 0;
3338 return executedMenuId;
3341 /***********************************************************************
3342 * MENU_InitTracking
3344 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3346 POPUPMENU *menu;
3348 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3350 HideCaret(0);
3352 if (!(menu = MENU_GetMenu( hMenu ))) return FALSE;
3354 /* This makes the menus of applications built with Delphi work.
3355 * It also enables menus to be displayed in more than one window,
3356 * but there are some bugs left that need to be fixed in this case.
3358 if (!bPopup) menu->hWnd = hWnd;
3359 if (!top_popup)
3361 top_popup = menu->hWnd;
3362 top_popup_hmenu = hMenu;
3365 fEndMenu = FALSE;
3367 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3368 if (!(wFlags & TPM_NONOTIFY))
3369 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3371 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3373 if (!(wFlags & TPM_NONOTIFY))
3375 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3376 /* If an app changed/recreated menu bar entries in WM_INITMENU
3377 * menu sizes will be recalculated once the menu created/shown.
3381 return TRUE;
3384 /***********************************************************************
3385 * MENU_ExitTracking
3387 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3389 TRACE("hwnd=%p\n", hWnd);
3391 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3392 ShowCaret(0);
3393 top_popup = 0;
3394 top_popup_hmenu = NULL;
3395 return TRUE;
3398 /***********************************************************************
3399 * MENU_TrackMouseMenuBar
3401 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3403 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3405 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3406 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3408 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3410 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3411 if (IsMenu(hMenu))
3413 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3415 /* fetch the window menu again, it may have changed */
3416 hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3417 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3418 MENU_ExitTracking(hWnd, FALSE);
3423 /***********************************************************************
3424 * MENU_TrackKbdMenuBar
3426 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3428 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3430 UINT uItem = NO_SELECTED_ITEM;
3431 HMENU hTrackMenu;
3432 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3434 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3436 /* find window that has a menu */
3438 while (is_win_menu_disallowed(hwnd))
3439 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3441 /* check if we have to track a system menu */
3443 hTrackMenu = GetMenu( hwnd );
3444 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3446 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3447 hTrackMenu = get_win_sys_menu( hwnd );
3448 uItem = 0;
3449 wParam |= HTSYSMENU; /* prevent item lookup */
3451 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3453 if (!IsMenu( hTrackMenu )) return;
3455 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3457 /* fetch the window menu again, it may have changed */
3458 hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
3460 if( wChar && wChar != ' ' )
3462 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3463 if ( uItem >= (UINT)(-2) )
3465 if( uItem == (UINT)(-1) ) MessageBeep(0);
3466 /* schedule end of menu tracking */
3467 wFlags |= TF_ENDMENU;
3468 goto track_menu;
3472 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3474 if (!(wParam & HTSYSMENU) || wChar == ' ')
3476 if( uItem == NO_SELECTED_ITEM )
3477 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3478 else
3479 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3482 track_menu:
3483 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3484 MENU_ExitTracking( hwnd, FALSE );
3487 /**********************************************************************
3488 * TrackPopupMenuEx (USER32.@)
3490 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3491 HWND hWnd, LPTPMPARAMS lpTpm )
3493 POPUPMENU *menu;
3494 BOOL ret = FALSE;
3496 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3497 hMenu, wFlags, x, y, hWnd, lpTpm,
3498 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3500 /* Parameter check */
3501 /* FIXME: this check is performed several times, here and in the called
3502 functions. That could be optimized */
3503 if (!(menu = MENU_GetMenu( hMenu )))
3505 SetLastError( ERROR_INVALID_MENU_HANDLE );
3506 return FALSE;
3509 if (IsWindow(menu->hWnd))
3511 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3512 return FALSE;
3515 if (MENU_InitPopup( hWnd, hMenu, wFlags ))
3517 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3519 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3520 if (!(wFlags & TPM_NONOTIFY))
3521 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3523 if (menu->wFlags & MF_SYSMENU)
3524 MENU_InitSysMenuPopup( hMenu, GetWindowLongW( hWnd, GWL_STYLE ),
3525 GetClassLongW( hWnd, GCL_STYLE));
3527 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3528 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3529 lpTpm ? &lpTpm->rcExclude : NULL );
3530 MENU_ExitTracking(hWnd, TRUE);
3532 if (menu->hWnd)
3534 DestroyWindow( menu->hWnd );
3535 menu->hWnd = 0;
3537 if (!(wFlags & TPM_NONOTIFY))
3538 SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
3539 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3541 SetLastError(0);
3544 return ret;
3547 /**********************************************************************
3548 * TrackPopupMenu (USER32.@)
3550 * Like the win32 API, the function return the command ID only if the
3551 * flag TPM_RETURNCMD is on.
3554 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3555 INT nReserved, HWND hWnd, const RECT *lpRect )
3557 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3560 /***********************************************************************
3561 * PopupMenuWndProc
3563 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3565 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3567 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3569 switch(message)
3571 case WM_CREATE:
3573 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3574 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3575 return 0;
3578 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3579 return MA_NOACTIVATE;
3581 case WM_PAINT:
3583 PAINTSTRUCT ps;
3584 BeginPaint( hwnd, &ps );
3585 MENU_DrawPopupMenu( hwnd, ps.hdc,
3586 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3587 EndPaint( hwnd, &ps );
3588 return 0;
3591 case WM_PRINTCLIENT:
3593 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3594 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3595 return 0;
3598 case WM_ERASEBKGND:
3599 return 1;
3601 case WM_DESTROY:
3602 /* zero out global pointer in case resident popup window was destroyed. */
3603 if (hwnd == top_popup) {
3604 top_popup = 0;
3605 top_popup_hmenu = NULL;
3607 break;
3609 case WM_SHOWWINDOW:
3611 if( wParam )
3613 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3615 else
3616 SetWindowLongPtrW( hwnd, 0, 0 );
3617 break;
3619 case MN_GETHMENU:
3620 return GetWindowLongPtrW( hwnd, 0 );
3622 default:
3623 return DefWindowProcW( hwnd, message, wParam, lParam );
3625 return 0;
3629 /***********************************************************************
3630 * MENU_GetMenuBarHeight
3632 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3634 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3635 INT orgX, INT orgY )
3637 HDC hdc;
3638 RECT rectBar;
3639 LPPOPUPMENU lppop;
3641 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3643 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3645 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3646 SelectObject( hdc, get_menu_font(FALSE));
3647 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3648 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3649 ReleaseDC( hwnd, hdc );
3650 return lppop->Height;
3654 /*******************************************************************
3655 * ChangeMenuA (USER32.@)
3657 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3658 UINT id, UINT flags )
3660 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3661 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3662 id, data );
3663 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3664 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3665 id, data );
3666 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3667 flags & MF_BYPOSITION ? pos : id,
3668 flags & ~MF_REMOVE );
3669 /* Default: MF_INSERT */
3670 return InsertMenuA( hMenu, pos, flags, id, data );
3674 /*******************************************************************
3675 * ChangeMenuW (USER32.@)
3677 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3678 UINT id, UINT flags )
3680 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3681 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3682 id, data );
3683 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3684 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3685 id, data );
3686 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3687 flags & MF_BYPOSITION ? pos : id,
3688 flags & ~MF_REMOVE );
3689 /* Default: MF_INSERT */
3690 return InsertMenuW( hMenu, pos, flags, id, data );
3694 /*******************************************************************
3695 * CheckMenuItem (USER32.@)
3697 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3699 POPUPMENU *menu;
3700 MENUITEM *item;
3701 DWORD ret;
3702 UINT pos;
3704 if (!(menu = find_menu_item(hMenu, id, flags, &pos)))
3705 return -1;
3706 item = &menu->items[pos];
3708 ret = item->fState & MF_CHECKED;
3709 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3710 else item->fState &= ~MF_CHECKED;
3711 release_menu_ptr(menu);
3712 return ret;
3716 /**********************************************************************
3717 * EnableMenuItem (USER32.@)
3719 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT id, UINT wFlags )
3721 UINT oldflags, pos;
3722 POPUPMENU *menu;
3723 MENUITEM *item;
3725 TRACE("(%p, %04x, %04x)\n", hMenu, id, wFlags);
3727 /* Get the Popupmenu to access the owner menu */
3728 if (!(menu = find_menu_item(hMenu, id, wFlags, &pos)))
3729 return (UINT)-1;
3731 item = &menu->items[pos];
3732 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3733 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3735 /* If the close item in the system menu change update the close button */
3736 if ((item->wID == SC_CLOSE) && (oldflags != wFlags) && menu->hSysMenuOwner)
3738 RECT rc;
3739 POPUPMENU* parentMenu;
3740 HWND hwnd;
3742 /* Get the parent menu to access */
3743 parentMenu = grab_menu_ptr(menu->hSysMenuOwner);
3744 release_menu_ptr(menu);
3745 if (!parentMenu)
3746 return (UINT)-1;
3748 hwnd = parentMenu->hWnd;
3749 release_menu_ptr(parentMenu);
3751 /* Refresh the frame to reflect the change */
3752 WIN_GetRectangles(hwnd, COORDS_CLIENT, &rc, NULL);
3753 rc.bottom = 0;
3754 RedrawWindow(hwnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3756 else
3757 release_menu_ptr(menu);
3759 return oldflags;
3763 /*******************************************************************
3764 * GetMenuStringA (USER32.@)
3766 INT WINAPI GetMenuStringA(
3767 HMENU hMenu, /* [in] menuhandle */
3768 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3769 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3770 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3771 UINT wFlags /* [in] MF_ flags */
3774 POPUPMENU *menu;
3775 MENUITEM *item;
3776 UINT pos;
3777 INT ret;
3779 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3780 if (str && nMaxSiz) str[0] = '\0';
3782 if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos)))
3784 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3785 return 0;
3787 item = &menu->items[pos];
3789 if (!item->text)
3790 ret = 0;
3791 else if (!str || !nMaxSiz)
3792 ret = WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, 0, NULL, NULL );
3793 else
3795 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3796 str[nMaxSiz-1] = 0;
3797 ret = strlen(str);
3799 release_menu_ptr(menu);
3801 TRACE("returning %s\n", debugstr_a(str));
3802 return ret;
3806 /*******************************************************************
3807 * GetMenuStringW (USER32.@)
3809 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3810 LPWSTR str, INT nMaxSiz, UINT wFlags )
3812 POPUPMENU *menu;
3813 MENUITEM *item;
3814 UINT pos;
3815 INT ret;
3817 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3818 if (str && nMaxSiz) str[0] = '\0';
3820 if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos)))
3822 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3823 return 0;
3825 item = &menu->items[pos];
3827 if (!str || !nMaxSiz)
3828 ret = item->text ? lstrlenW(item->text) : 0;
3829 else if (!item->text)
3831 str[0] = 0;
3832 ret = 0;
3834 else
3836 lstrcpynW( str, item->text, nMaxSiz );
3837 ret = lstrlenW(str);
3839 release_menu_ptr(menu);
3841 TRACE("returning %s\n", debugstr_w(str));
3842 return ret;
3846 /**********************************************************************
3847 * HiliteMenuItem (USER32.@)
3849 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3850 UINT wHilite )
3852 POPUPMENU *menu;
3853 UINT pos;
3854 HMENU handle_menu;
3855 UINT focused_item;
3857 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3859 if (!(menu = find_menu_item(hMenu, wItemID, wHilite, &pos))) return FALSE;
3861 handle_menu = menu->obj.handle;
3862 focused_item = menu->FocusedItem;
3863 release_menu_ptr(menu);
3865 if (focused_item != pos)
3867 MENU_HideSubPopups( hWnd, handle_menu, FALSE, 0 );
3868 MENU_SelectItem( hWnd, handle_menu, pos, TRUE, 0 );
3871 return TRUE;
3875 /**********************************************************************
3876 * GetMenuState (USER32.@)
3878 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3880 POPUPMENU *menu;
3881 UINT state, pos;
3882 MENUITEM *item;
3884 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3886 if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos)))
3887 return -1;
3889 item = &menu->items[pos];
3890 debug_print_menuitem (" item: ", item, "");
3891 if (item->fType & MF_POPUP)
3893 POPUPMENU *submenu = grab_menu_ptr(item->hSubMenu);
3894 if (submenu)
3895 state = (submenu->nItems << 8) | ((item->fState | item->fType) & 0xff);
3896 else
3897 state = -1;
3898 release_menu_ptr(submenu);
3900 else
3902 /* We used to (from way back then) mask the result to 0xff. */
3903 /* I don't know why and it seems wrong as the documented */
3904 /* return flag MF_SEPARATOR is outside that mask. */
3905 state = (item->fType | item->fState);
3907 release_menu_ptr(menu);
3908 return state;
3912 /**********************************************************************
3913 * GetMenuItemCount (USER32.@)
3915 INT WINAPI GetMenuItemCount( HMENU hMenu )
3917 POPUPMENU *menu = grab_menu_ptr(hMenu);
3918 INT count;
3920 if (!menu) return -1;
3921 count = menu->nItems;
3922 release_menu_ptr(menu);
3924 TRACE("(%p) returning %d\n", hMenu, count);
3925 return count;
3929 /**********************************************************************
3930 * GetMenuItemID (USER32.@)
3932 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3934 POPUPMENU *menu;
3935 UINT id, pos;
3937 if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos)))
3938 return -1;
3940 id = menu->items[pos].fType & MF_POPUP ? -1 : menu->items[pos].wID;
3941 release_menu_ptr(menu);
3942 return id;
3946 /**********************************************************************
3947 * MENU_mnu2mnuii
3949 * Uses flags, id and text ptr, passed by InsertMenu() and
3950 * ModifyMenu() to setup a MenuItemInfo structure.
3952 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3953 LPMENUITEMINFOW pmii)
3955 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3956 pmii->cbSize = sizeof( MENUITEMINFOW);
3957 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3958 /* setting bitmap clears text and vice versa */
3959 if( IS_STRING_ITEM(flags)) {
3960 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3961 if( !str)
3962 flags |= MF_SEPARATOR;
3963 /* Item beginning with a backspace is a help item */
3964 /* FIXME: wrong place, this is only true in win16 */
3965 else if( *str == '\b') {
3966 flags |= MF_HELP;
3967 str++;
3969 pmii->dwTypeData = (LPWSTR)str;
3970 } else if( flags & MFT_BITMAP){
3971 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3972 pmii->hbmpItem = (HBITMAP)str;
3974 if( flags & MF_OWNERDRAW){
3975 pmii->fMask |= MIIM_DATA;
3976 pmii->dwItemData = (ULONG_PTR) str;
3978 if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
3979 pmii->fMask |= MIIM_SUBMENU;
3980 pmii->hSubMenu = (HMENU)id;
3982 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3983 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3984 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3985 pmii->wID = (UINT)id;
3989 /*******************************************************************
3990 * InsertMenuW (USER32.@)
3992 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3993 UINT_PTR id, LPCWSTR str )
3995 MENUITEMINFOW mii;
3996 POPUPMENU *menu;
3997 MENUITEM *item;
3998 UINT newpos;
3999 BOOL ret;
4001 if (IS_STRING_ITEM(flags) && str)
4002 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
4003 hMenu, pos, flags, id, debugstr_w(str) );
4004 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
4005 hMenu, pos, flags, id, str );
4007 if (!(menu = insert_menu_item(hMenu, pos, flags, &newpos)))
4008 return FALSE;
4010 MENU_mnu2mnuii( flags, id, str, &mii);
4012 item = &menu->items[newpos];
4013 ret = SetMenuItemInfo_common( item, &mii, TRUE);
4014 if (ret)
4015 item->hCheckBit = item->hUnCheckBit = 0;
4016 else
4017 RemoveMenu( hMenu, pos, flags );
4018 release_menu_ptr(menu);
4020 return ret;
4024 /*******************************************************************
4025 * InsertMenuA (USER32.@)
4027 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
4028 UINT_PTR id, LPCSTR str )
4030 BOOL ret = FALSE;
4032 if (IS_STRING_ITEM(flags) && str)
4034 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4035 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
4036 if (newstr)
4038 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
4039 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
4040 HeapFree( GetProcessHeap(), 0, newstr );
4042 return ret;
4044 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4048 /*******************************************************************
4049 * AppendMenuA (USER32.@)
4051 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
4052 UINT_PTR id, LPCSTR data )
4054 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
4058 /*******************************************************************
4059 * AppendMenuW (USER32.@)
4061 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
4062 UINT_PTR id, LPCWSTR data )
4064 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
4068 /**********************************************************************
4069 * RemoveMenu (USER32.@)
4071 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT id, UINT flags )
4073 POPUPMENU *menu;
4074 UINT pos;
4076 TRACE("(menu=%p id=%#x flags=%04x)\n", hMenu, id, flags);
4078 if (!(menu = find_menu_item(hMenu, id, flags, &pos)))
4079 return FALSE;
4081 /* Remove item */
4082 MENU_FreeItemData( &menu->items[pos] );
4084 if (--menu->nItems == 0)
4086 HeapFree( GetProcessHeap(), 0, menu->items );
4087 menu->items = NULL;
4089 else
4091 MENUITEM *new_items, *item = &menu->items[pos];
4093 while (pos < menu->nItems)
4095 *item = *(item+1);
4096 item++;
4097 pos++;
4099 new_items = HeapReAlloc( GetProcessHeap(), 0, menu->items, menu->nItems * sizeof(MENUITEM) );
4100 if (new_items) menu->items = new_items;
4102 release_menu_ptr(menu);
4104 return TRUE;
4108 /**********************************************************************
4109 * DeleteMenu (USER32.@)
4111 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT id, UINT flags )
4113 POPUPMENU *menu;
4114 UINT pos;
4116 if (!(menu = find_menu_item(hMenu, id, flags, &pos)))
4117 return FALSE;
4119 if (menu->items[pos].fType & MF_POPUP)
4120 DestroyMenu(menu->items[pos].hSubMenu);
4122 RemoveMenu(menu->obj.handle, pos, flags | MF_BYPOSITION);
4123 release_menu_ptr(menu);
4124 return TRUE;
4128 /*******************************************************************
4129 * ModifyMenuW (USER32.@)
4131 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
4132 UINT_PTR id, LPCWSTR str )
4134 MENUITEMINFOW mii;
4135 POPUPMENU *menu;
4136 UINT item_pos;
4137 BOOL ret;
4139 if (IS_STRING_ITEM(flags))
4140 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
4141 else
4142 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
4144 if (!(menu = find_menu_item(hMenu, pos, flags, &item_pos)))
4146 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4147 if (pos == SC_TASKLIST && !(flags & MF_BYPOSITION)) return TRUE;
4148 return FALSE;
4150 menu->Height = 0; /* force size recalculate */
4151 MENU_mnu2mnuii( flags, id, str, &mii);
4152 ret = SetMenuItemInfo_common(&menu->items[item_pos], &mii, TRUE);
4153 release_menu_ptr(menu);
4154 return ret;
4158 /*******************************************************************
4159 * ModifyMenuA (USER32.@)
4161 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
4162 UINT_PTR id, LPCSTR str )
4164 BOOL ret = FALSE;
4166 if (IS_STRING_ITEM(flags) && str)
4168 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4169 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
4170 if (newstr)
4172 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
4173 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
4174 HeapFree( GetProcessHeap(), 0, newstr );
4176 return ret;
4178 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4182 /**********************************************************************
4183 * CreatePopupMenu (USER32.@)
4185 HMENU WINAPI CreatePopupMenu(void)
4187 HMENU hmenu;
4188 POPUPMENU *menu;
4190 if (!(hmenu = CreateMenu())) return 0;
4191 menu = MENU_GetMenu( hmenu );
4192 menu->wFlags |= MF_POPUP;
4193 return hmenu;
4197 /**********************************************************************
4198 * GetMenuCheckMarkDimensions (USER.417)
4199 * GetMenuCheckMarkDimensions (USER32.@)
4201 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4203 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4207 /**********************************************************************
4208 * SetMenuItemBitmaps (USER32.@)
4210 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4211 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4213 POPUPMENU *menu;
4214 MENUITEM *item;
4215 UINT pos;
4217 if (!(menu = find_menu_item(hMenu, nPos, wFlags, &pos)))
4218 return FALSE;
4220 item = &menu->items[pos];
4221 if (!hNewCheck && !hNewUnCheck)
4223 item->fState &= ~MF_USECHECKBITMAPS;
4225 else /* Install new bitmaps */
4227 item->hCheckBit = hNewCheck;
4228 item->hUnCheckBit = hNewUnCheck;
4229 item->fState |= MF_USECHECKBITMAPS;
4231 release_menu_ptr(menu);
4233 return TRUE;
4237 /**********************************************************************
4238 * CreateMenu (USER32.@)
4240 HMENU WINAPI CreateMenu(void)
4242 HMENU hMenu;
4243 LPPOPUPMENU menu;
4245 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4246 menu->FocusedItem = NO_SELECTED_ITEM;
4247 menu->refcount = 1;
4249 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4251 TRACE("return %p\n", hMenu );
4253 return hMenu;
4257 /**********************************************************************
4258 * DestroyMenu (USER32.@)
4260 BOOL WINAPI DestroyMenu( HMENU hMenu )
4262 LPPOPUPMENU lppop;
4264 TRACE("(%p)\n", hMenu);
4266 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4267 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4269 /* DestroyMenu should not destroy system menu popup owner */
4270 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4272 DestroyWindow( lppop->hWnd );
4273 lppop->hWnd = 0;
4276 if (lppop->items) /* recursively destroy submenus */
4278 int i;
4279 MENUITEM *item = lppop->items;
4280 for (i = lppop->nItems; i > 0; i--, item++)
4282 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4283 MENU_FreeItemData( item );
4285 HeapFree( GetProcessHeap(), 0, lppop->items );
4287 HeapFree( GetProcessHeap(), 0, lppop );
4288 return TRUE;
4292 /**********************************************************************
4293 * GetSystemMenu (USER32.@)
4295 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4297 WND *wndPtr = WIN_GetPtr( hWnd );
4298 HMENU retvalue = 0;
4300 if (wndPtr == WND_DESKTOP) return 0;
4301 if (wndPtr == WND_OTHER_PROCESS)
4303 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4305 else if (wndPtr)
4307 if (wndPtr->hSysMenu && bRevert)
4309 DestroyMenu(wndPtr->hSysMenu);
4310 wndPtr->hSysMenu = 0;
4313 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4314 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4316 if( wndPtr->hSysMenu )
4318 POPUPMENU *menu;
4319 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4321 /* Store the dummy sysmenu handle to facilitate the refresh */
4322 /* of the close button if the SC_CLOSE item change */
4323 menu = MENU_GetMenu(retvalue);
4324 if ( menu )
4325 menu->hSysMenuOwner = wndPtr->hSysMenu;
4327 WIN_ReleasePtr( wndPtr );
4329 return bRevert ? 0 : retvalue;
4333 /*******************************************************************
4334 * SetSystemMenu (USER32.@)
4336 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4338 WND *wndPtr = WIN_GetPtr( hwnd );
4340 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4342 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4343 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4344 WIN_ReleasePtr( wndPtr );
4345 return TRUE;
4347 return FALSE;
4351 /**********************************************************************
4352 * GetMenu (USER32.@)
4354 HMENU WINAPI GetMenu( HWND hWnd )
4356 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4357 TRACE("for %p returning %p\n", hWnd, retvalue);
4358 return retvalue;
4361 /**********************************************************************
4362 * GetMenuBarInfo (USER32.@)
4364 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4366 POPUPMENU *menu;
4367 HMENU hmenu = NULL;
4368 ATOM class_atom;
4370 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4372 switch (idObject)
4374 case OBJID_CLIENT:
4375 class_atom = GetClassLongW(hwnd, GCW_ATOM);
4376 if (!class_atom)
4377 return FALSE;
4378 if (class_atom != POPUPMENU_CLASS_ATOM)
4380 WARN("called on invalid window: %d\n", class_atom);
4381 SetLastError(ERROR_INVALID_MENU_HANDLE);
4382 return FALSE;
4385 hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
4386 break;
4387 case OBJID_MENU:
4388 hmenu = GetMenu(hwnd);
4389 break;
4390 case OBJID_SYSMENU:
4391 hmenu = GetSystemMenu(hwnd, FALSE);
4392 break;
4393 default:
4394 return FALSE;
4397 if (!hmenu)
4398 return FALSE;
4400 if (pmbi->cbSize != sizeof(MENUBARINFO))
4402 SetLastError(ERROR_INVALID_PARAMETER);
4403 return FALSE;
4406 menu = MENU_GetMenu(hmenu);
4407 if (!menu)
4408 return FALSE;
4409 if (idItem < 0 || idItem > menu->nItems)
4410 return FALSE;
4412 if (!menu->Height)
4414 SetRectEmpty(&pmbi->rcBar);
4416 else if (idItem == 0)
4418 GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar);
4419 pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
4420 pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
4422 else
4424 GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar);
4427 pmbi->hMenu = hmenu;
4428 pmbi->hwndMenu = NULL;
4429 pmbi->fBarFocused = top_popup_hmenu == hmenu;
4430 if (idItem)
4432 pmbi->fFocused = menu->FocusedItem == idItem - 1;
4433 if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
4435 menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
4436 if (menu)
4437 pmbi->hwndMenu = menu->hWnd;
4440 else
4442 pmbi->fFocused = pmbi->fBarFocused;
4445 return TRUE;
4448 /**********************************************************************
4449 * MENU_SetMenu
4451 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4452 * SetWindowPos call that would result if SetMenu were called directly.
4454 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4456 TRACE("(%p, %p);\n", hWnd, hMenu);
4458 if (hMenu && !IsMenu(hMenu))
4460 WARN("hMenu %p is not a menu handle\n", hMenu);
4461 return FALSE;
4463 if (is_win_menu_disallowed(hWnd))
4464 return FALSE;
4466 hWnd = WIN_GetFullHandle( hWnd );
4467 if (GetCapture() == hWnd)
4468 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4470 if (hMenu)
4472 POPUPMENU *menu;
4474 if (!(menu = grab_menu_ptr(hMenu))) return FALSE;
4475 menu->hWnd = hWnd;
4476 menu->Height = 0; /* Make sure we recalculate the size */
4477 release_menu_ptr(menu);
4479 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4480 return TRUE;
4484 /**********************************************************************
4485 * SetMenu (USER32.@)
4487 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4489 if(!MENU_SetMenu(hWnd, hMenu))
4490 return FALSE;
4492 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4493 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4494 return TRUE;
4498 /**********************************************************************
4499 * GetSubMenu (USER32.@)
4501 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4503 POPUPMENU *menu;
4504 HMENU submenu;
4505 UINT pos;
4507 if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos)))
4508 return 0;
4510 if (menu->items[pos].fType & MF_POPUP)
4511 submenu = menu->items[pos].hSubMenu;
4512 else
4513 submenu = 0;
4515 release_menu_ptr(menu);
4516 return submenu;
4520 /**********************************************************************
4521 * DrawMenuBar (USER32.@)
4523 BOOL WINAPI DrawMenuBar( HWND hWnd )
4525 HMENU hMenu;
4527 if (!IsWindow( hWnd ))
4528 return FALSE;
4529 if (is_win_menu_disallowed(hWnd))
4530 return TRUE;
4532 if ((hMenu = GetMenu( hWnd )))
4534 POPUPMENU *menu = grab_menu_ptr(hMenu);
4535 if (menu)
4537 menu->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4538 menu->hwndOwner = hWnd;
4539 release_menu_ptr(menu);
4543 return SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4544 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4547 /***********************************************************************
4548 * DrawMenuBarTemp (USER32.@)
4550 * UNDOCUMENTED !!
4552 * called by W98SE desk.cpl Control Panel Applet
4554 * Not 100% sure about the param names, but close.
4556 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4558 LPPOPUPMENU lppop;
4559 UINT i,retvalue;
4560 HFONT hfontOld = 0;
4561 BOOL flat_menu = FALSE;
4563 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4565 if (!hMenu)
4566 hMenu = GetMenu(hwnd);
4568 if (!hFont)
4569 hFont = get_menu_font(FALSE);
4571 lppop = MENU_GetMenu( hMenu );
4572 if (lppop == NULL || lprect == NULL)
4574 retvalue = GetSystemMetrics(SM_CYMENU);
4575 goto END;
4578 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4580 hfontOld = SelectObject( hDC, hFont);
4582 if (lppop->Height == 0)
4583 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4585 lprect->bottom = lprect->top + lppop->Height;
4587 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4589 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4590 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4591 LineTo( hDC, lprect->right, lprect->bottom );
4593 if (lppop->nItems == 0)
4595 retvalue = GetSystemMetrics(SM_CYMENU);
4596 goto END;
4599 for (i = 0; i < lppop->nItems; i++)
4600 MENU_DrawMenuItem( hwnd, lppop, hwnd, hDC, &lppop->items[i], TRUE, ODA_DRAWENTIRE );
4602 retvalue = lppop->Height;
4604 END:
4605 if (hfontOld) SelectObject (hDC, hfontOld);
4606 return retvalue;
4609 /***********************************************************************
4610 * EndMenu (USER.187)
4611 * EndMenu (USER32.@)
4613 BOOL WINAPI EndMenu(void)
4615 /* if we are in the menu code, and it is active */
4616 if (!fEndMenu && top_popup)
4618 /* terminate the menu handling code */
4619 fEndMenu = TRUE;
4621 /* needs to be posted to wakeup the internal menu handler */
4622 /* which will now terminate the menu, in the event that */
4623 /* the main window was minimized, or lost focus, so we */
4624 /* don't end up with an orphaned menu */
4625 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4627 return fEndMenu;
4631 /*****************************************************************
4632 * LoadMenuA (USER32.@)
4634 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4636 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4637 if (!hrsrc) return 0;
4638 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4642 /*****************************************************************
4643 * LoadMenuW (USER32.@)
4645 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4647 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4648 if (!hrsrc) return 0;
4649 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4653 /**********************************************************************
4654 * LoadMenuIndirectW (USER32.@)
4656 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4658 HMENU hMenu;
4659 WORD version, offset;
4660 LPCSTR p = template;
4662 version = GET_WORD(p);
4663 p += sizeof(WORD);
4664 TRACE("%p, ver %d\n", template, version );
4665 switch (version)
4667 case 0: /* standard format is version of 0 */
4668 offset = GET_WORD(p);
4669 p += sizeof(WORD) + offset;
4670 if (!(hMenu = CreateMenu())) return 0;
4671 if (!MENU_ParseResource( p, hMenu ))
4673 DestroyMenu( hMenu );
4674 return 0;
4676 return hMenu;
4677 case 1: /* extended format is version of 1 */
4678 offset = GET_WORD(p);
4679 p += sizeof(WORD) + offset;
4680 if (!(hMenu = CreateMenu())) return 0;
4681 if (!MENUEX_ParseResource( p, hMenu))
4683 DestroyMenu( hMenu );
4684 return 0;
4686 return hMenu;
4687 default:
4688 ERR("version %d not supported.\n", version);
4689 return 0;
4694 /**********************************************************************
4695 * LoadMenuIndirectA (USER32.@)
4697 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4699 return LoadMenuIndirectW( template );
4703 /**********************************************************************
4704 * IsMenu (USER32.@)
4706 BOOL WINAPI IsMenu(HMENU hmenu)
4708 POPUPMENU *menu;
4709 BOOL is_menu;
4711 menu = grab_menu_ptr(hmenu);
4712 is_menu = menu != NULL;
4713 release_menu_ptr(menu);
4715 if (!is_menu)
4716 SetLastError(ERROR_INVALID_MENU_HANDLE);
4718 return is_menu;
4721 /**********************************************************************
4722 * GetMenuItemInfo_common
4725 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT id, BOOL bypos,
4726 LPMENUITEMINFOW lpmii, BOOL unicode)
4728 POPUPMENU *menu;
4729 MENUITEM *item;
4730 UINT pos;
4732 menu = find_menu_item(hmenu, id, bypos ? MF_BYPOSITION : 0, &pos);
4734 item = menu ? &menu->items[pos] : NULL;
4736 debug_print_menuitem("GetMenuItemInfo_common: ", item, "");
4738 if (!menu)
4740 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4741 return FALSE;
4744 if( lpmii->fMask & MIIM_TYPE) {
4745 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4746 release_menu_ptr(menu);
4747 WARN("invalid combination of fMask bits used\n");
4748 /* this does not happen on Win9x/ME */
4749 SetLastError( ERROR_INVALID_PARAMETER);
4750 return FALSE;
4752 lpmii->fType = item->fType & MENUITEMINFO_TYPE_MASK;
4753 if (item->hbmpItem && !IS_MAGIC_BITMAP(item->hbmpItem))
4754 lpmii->fType |= MFT_BITMAP;
4755 lpmii->hbmpItem = item->hbmpItem; /* not on Win9x/ME */
4756 if( lpmii->fType & MFT_BITMAP) {
4757 lpmii->dwTypeData = (LPWSTR) item->hbmpItem;
4758 lpmii->cch = 0;
4759 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4760 /* this does not happen on Win9x/ME */
4761 lpmii->dwTypeData = 0;
4762 lpmii->cch = 0;
4766 /* copy the text string */
4767 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4768 if (!item->text) {
4769 if(lpmii->dwTypeData && lpmii->cch) {
4770 if( unicode)
4771 *((WCHAR *)lpmii->dwTypeData) = 0;
4772 else
4773 *((CHAR *)lpmii->dwTypeData) = 0;
4775 lpmii->cch = 0;
4776 } else {
4777 int len;
4778 if (unicode)
4780 len = lstrlenW(item->text);
4781 if(lpmii->dwTypeData && lpmii->cch)
4782 lstrcpynW(lpmii->dwTypeData, item->text, lpmii->cch);
4784 else
4786 len = WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL,
4787 0, NULL, NULL ) - 1;
4788 if(lpmii->dwTypeData && lpmii->cch)
4789 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1,
4790 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4791 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4793 /* if we've copied a substring we return its length */
4794 if(lpmii->dwTypeData && lpmii->cch)
4795 if (lpmii->cch <= len + 1)
4796 lpmii->cch--;
4797 else
4798 lpmii->cch = len;
4799 else {
4800 /* return length of string */
4801 /* not on Win9x/ME if fType & MFT_BITMAP */
4802 lpmii->cch = len;
4807 if (lpmii->fMask & MIIM_FTYPE)
4808 lpmii->fType = item->fType & MENUITEMINFO_TYPE_MASK;
4810 if (lpmii->fMask & MIIM_BITMAP)
4811 lpmii->hbmpItem = item->hbmpItem;
4813 if (lpmii->fMask & MIIM_STATE)
4814 lpmii->fState = item->fState & MENUITEMINFO_STATE_MASK;
4816 if (lpmii->fMask & MIIM_ID)
4817 lpmii->wID = item->wID;
4819 if (lpmii->fMask & MIIM_SUBMENU)
4820 lpmii->hSubMenu = item->hSubMenu;
4821 else {
4822 /* hSubMenu is always cleared
4823 * (not on Win9x/ME ) */
4824 lpmii->hSubMenu = 0;
4827 if (lpmii->fMask & MIIM_CHECKMARKS) {
4828 lpmii->hbmpChecked = item->hCheckBit;
4829 lpmii->hbmpUnchecked = item->hUnCheckBit;
4831 if (lpmii->fMask & MIIM_DATA)
4832 lpmii->dwItemData = item->dwItemData;
4834 release_menu_ptr(menu);
4835 return TRUE;
4838 /**********************************************************************
4839 * GetMenuItemInfoA (USER32.@)
4841 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4842 LPMENUITEMINFOA lpmii)
4844 BOOL ret;
4845 MENUITEMINFOA mii;
4846 if( lpmii->cbSize != sizeof( mii) &&
4847 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4848 SetLastError( ERROR_INVALID_PARAMETER);
4849 return FALSE;
4851 memcpy( &mii, lpmii, lpmii->cbSize);
4852 mii.cbSize = sizeof( mii);
4853 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4854 (LPMENUITEMINFOW)&mii, FALSE);
4855 mii.cbSize = lpmii->cbSize;
4856 memcpy( lpmii, &mii, mii.cbSize);
4857 return ret;
4860 /**********************************************************************
4861 * GetMenuItemInfoW (USER32.@)
4863 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4864 LPMENUITEMINFOW lpmii)
4866 BOOL ret;
4867 MENUITEMINFOW mii;
4868 if( lpmii->cbSize != sizeof( mii) &&
4869 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4870 SetLastError( ERROR_INVALID_PARAMETER);
4871 return FALSE;
4873 memcpy( &mii, lpmii, lpmii->cbSize);
4874 mii.cbSize = sizeof( mii);
4875 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4876 mii.cbSize = lpmii->cbSize;
4877 memcpy( lpmii, &mii, mii.cbSize);
4878 return ret;
4882 /* set a menu item text from a ASCII or Unicode string */
4883 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4885 if (!text)
4886 menu->text = NULL;
4887 else if (unicode)
4889 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(text)+1) * sizeof(WCHAR) )))
4890 lstrcpyW( menu->text, text );
4892 else
4894 LPCSTR str = (LPCSTR)text;
4895 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4896 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4897 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4902 /**********************************************************************
4903 * MENU_depth
4905 * detect if there are loops in the menu tree (or the depth is too large)
4907 static int MENU_depth( POPUPMENU *pmenu, int depth)
4909 UINT i;
4910 MENUITEM *item;
4911 int subdepth;
4913 depth++;
4914 if( depth > MAXMENUDEPTH) return depth;
4915 item = pmenu->items;
4916 subdepth = depth;
4917 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4918 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4919 if( psubmenu){
4920 int bdepth = MENU_depth( psubmenu, depth);
4921 if( bdepth > subdepth) subdepth = bdepth;
4923 if( subdepth > MAXMENUDEPTH)
4924 TRACE("<- hmenu %p\n", item->hSubMenu);
4926 return subdepth;
4930 /**********************************************************************
4931 * SetMenuItemInfo_common
4933 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4934 * MIIM_BITMAP and MIIM_STRING flags instead.
4937 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4938 const MENUITEMINFOW *lpmii,
4939 BOOL unicode)
4941 if (!menu) return FALSE;
4943 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4945 if (lpmii->fMask & MIIM_FTYPE ) {
4946 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4947 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4949 if (lpmii->fMask & MIIM_STRING ) {
4950 /* free the string when used */
4951 HeapFree(GetProcessHeap(), 0, menu->text);
4952 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4955 if (lpmii->fMask & MIIM_STATE)
4956 /* Other menu items having MFS_DEFAULT are not converted
4957 to normal items */
4958 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4960 if (lpmii->fMask & MIIM_ID)
4961 menu->wID = lpmii->wID;
4963 if (lpmii->fMask & MIIM_SUBMENU) {
4964 menu->hSubMenu = lpmii->hSubMenu;
4965 if (menu->hSubMenu) {
4966 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4967 if (subMenu) {
4968 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4969 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4970 menu->hSubMenu = 0;
4971 return FALSE;
4973 subMenu->wFlags |= MF_POPUP;
4974 menu->fType |= MF_POPUP;
4975 } else {
4976 SetLastError( ERROR_INVALID_PARAMETER);
4977 return FALSE;
4980 else
4981 menu->fType &= ~MF_POPUP;
4984 if (lpmii->fMask & MIIM_CHECKMARKS)
4986 menu->hCheckBit = lpmii->hbmpChecked;
4987 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4989 if (lpmii->fMask & MIIM_DATA)
4990 menu->dwItemData = lpmii->dwItemData;
4992 if (lpmii->fMask & MIIM_BITMAP)
4993 menu->hbmpItem = lpmii->hbmpItem;
4995 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4996 menu->fType |= MFT_SEPARATOR;
4998 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4999 return TRUE;
5002 /**********************************************************************
5003 * MENU_NormalizeMenuItemInfoStruct
5005 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
5006 * check, copy and extend the MENUITEMINFO struct from the version that the application
5007 * supplied to the version used by wine source. */
5008 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
5009 MENUITEMINFOW *pmii_out )
5011 /* do we recognize the size? */
5012 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
5013 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
5014 SetLastError( ERROR_INVALID_PARAMETER);
5015 return FALSE;
5017 /* copy the fields that we have */
5018 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
5019 /* if the hbmpItem member is missing then extend */
5020 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
5021 pmii_out->cbSize = sizeof( MENUITEMINFOW);
5022 pmii_out->hbmpItem = NULL;
5024 /* test for invalid bit combinations */
5025 if( (pmii_out->fMask & MIIM_TYPE &&
5026 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
5027 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
5028 WARN("invalid combination of fMask bits used\n");
5029 /* this does not happen on Win9x/ME */
5030 SetLastError( ERROR_INVALID_PARAMETER);
5031 return FALSE;
5033 /* convert old style (MIIM_TYPE) to the new */
5034 if( pmii_out->fMask & MIIM_TYPE){
5035 pmii_out->fMask |= MIIM_FTYPE;
5036 if( IS_STRING_ITEM(pmii_out->fType)){
5037 pmii_out->fMask |= MIIM_STRING;
5038 } else if( (pmii_out->fType) & MFT_BITMAP){
5039 pmii_out->fMask |= MIIM_BITMAP;
5040 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
5043 return TRUE;
5046 /**********************************************************************
5047 * SetMenuItemInfoA (USER32.@)
5049 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
5050 const MENUITEMINFOA *lpmii)
5052 MENUITEMINFOW mii;
5053 POPUPMENU *menu;
5054 UINT pos;
5055 BOOL ret;
5057 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
5059 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5061 if (!(menu = find_menu_item(hmenu, item, bypos ? MF_BYPOSITION : 0, &pos)))
5063 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
5064 if (item == SC_TASKLIST && !bypos) return TRUE;
5065 return FALSE;
5067 ret = SetMenuItemInfo_common(&menu->items[pos], &mii, FALSE);
5068 release_menu_ptr(menu);
5069 return ret;
5072 /**********************************************************************
5073 * SetMenuItemInfoW (USER32.@)
5075 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
5076 const MENUITEMINFOW *lpmii)
5078 MENUITEMINFOW mii;
5079 POPUPMENU *menu;
5080 BOOL ret;
5081 UINT pos;
5083 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
5085 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
5087 if (!(menu = find_menu_item(hmenu, item, bypos ? MF_BYPOSITION : 0, &pos)))
5089 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
5090 if (item == SC_TASKLIST && !bypos) return TRUE;
5091 return FALSE;
5094 ret = SetMenuItemInfo_common(&menu->items[pos], &mii, TRUE);
5095 release_menu_ptr(menu);
5096 return ret;
5099 static BOOL set_menu_default_item(POPUPMENU *menu, UINT uItem, UINT bypos)
5101 unsigned int i;
5102 MENUITEM *item;
5104 /* reset all default-item flags */
5105 item = menu->items;
5106 for (i = 0; i < menu->nItems; i++, item++)
5108 item->fState &= ~MFS_DEFAULT;
5111 /* no default item */
5112 if (-1 == uItem)
5113 return TRUE;
5115 item = menu->items;
5116 if ( bypos )
5118 if ( uItem >= menu->nItems ) return FALSE;
5119 item[uItem].fState |= MFS_DEFAULT;
5120 return TRUE;
5122 else
5124 for (i = 0; i < menu->nItems; i++, item++)
5126 if (item->wID == uItem)
5128 item->fState |= MFS_DEFAULT;
5129 return TRUE;
5134 return FALSE;
5137 /**********************************************************************
5138 * SetMenuDefaultItem (USER32.@)
5141 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
5143 POPUPMENU *menu;
5144 BOOL ret;
5146 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
5148 if (!(menu = grab_menu_ptr(hmenu))) return FALSE;
5149 ret = set_menu_default_item(menu, uItem, bypos);
5150 release_menu_ptr(menu);
5152 return ret;
5155 /**********************************************************************
5156 * GetMenuDefaultItem (USER32.@)
5158 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
5160 POPUPMENU *menu;
5161 MENUITEM * item;
5162 UINT i = 0;
5164 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
5166 if (!(menu = MENU_GetMenu(hmenu))) return -1;
5168 /* find default item */
5169 item = menu->items;
5171 /* empty menu */
5172 if (! item) return -1;
5174 while ( !( item->fState & MFS_DEFAULT ) )
5176 i++; item++;
5177 if (i >= menu->nItems ) return -1;
5180 /* default: don't return disabled items */
5181 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
5183 /* search rekursiv when needed */
5184 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
5186 UINT ret;
5187 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
5188 if ( -1 != ret ) return ret;
5190 /* when item not found in submenu, return the popup item */
5192 return ( bypos ) ? i : item->wID;
5197 /**********************************************************************
5198 * InsertMenuItemA (USER32.@)
5200 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
5201 const MENUITEMINFOA *lpmii)
5203 MENUITEMINFOW mii;
5204 POPUPMENU *menu;
5205 UINT pos;
5206 BOOL ret;
5208 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
5210 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5212 if (!(menu = insert_menu_item(hMenu, uItem, bypos ? MF_BYPOSITION : 0, &pos)))
5213 return FALSE;
5215 ret = SetMenuItemInfo_common(&menu->items[pos], &mii, FALSE);
5216 release_menu_ptr(menu);
5217 return ret;
5221 /**********************************************************************
5222 * InsertMenuItemW (USER32.@)
5224 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
5225 const MENUITEMINFOW *lpmii)
5227 MENUITEMINFOW mii;
5228 POPUPMENU *menu;
5229 UINT pos;
5230 BOOL ret;
5232 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
5234 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
5236 if (!(menu = insert_menu_item(hMenu, uItem, bypos ? MF_BYPOSITION : 0, &pos)))
5237 return FALSE;
5239 ret = SetMenuItemInfo_common(&menu->items[pos], &mii, TRUE);
5240 release_menu_ptr(menu);
5241 return ret;
5244 /**********************************************************************
5245 * CheckMenuRadioItem (USER32.@)
5248 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu, UINT first, UINT last,
5249 UINT check, UINT flags)
5251 POPUPMENU *first_menu = NULL, *check_menu;
5252 UINT i, check_pos;
5253 BOOL done = FALSE;
5255 for (i = first; i <= last; i++)
5257 MENUITEM *item;
5259 if (!(check_menu = find_menu_item(hMenu, i, flags, &check_pos)))
5260 continue;
5262 if (!first_menu)
5263 first_menu = grab_menu_ptr(check_menu->obj.handle);
5265 if (first_menu != check_menu)
5267 release_menu_ptr(check_menu);
5268 continue;
5271 item = &check_menu->items[check_pos];
5272 if (item->fType != MFT_SEPARATOR)
5274 if (i == check)
5276 item->fType |= MFT_RADIOCHECK;
5277 item->fState |= MFS_CHECKED;
5278 done = TRUE;
5280 else
5282 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5283 item->fState &= ~MFS_CHECKED;
5287 release_menu_ptr(check_menu);
5289 release_menu_ptr(first_menu);
5291 return done;
5295 /**********************************************************************
5296 * GetMenuItemRect (USER32.@)
5298 * ATTENTION: Here, the returned values in rect are the screen
5299 * coordinates of the item just like if the menu was
5300 * always on the upper left side of the application.
5303 BOOL WINAPI GetMenuItemRect(HWND hwnd, HMENU hMenu, UINT uItem, RECT *rect)
5305 POPUPMENU *menu;
5306 UINT pos;
5307 RECT window_rect;
5309 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5311 if (!rect)
5312 return FALSE;
5314 if (!(menu = find_menu_item(hMenu, uItem, MF_BYPOSITION, &pos)))
5315 return FALSE;
5317 if (!hwnd) hwnd = menu->hWnd;
5318 if (!hwnd)
5320 release_menu_ptr(menu);
5321 return FALSE;
5324 *rect = menu->items[pos].rect;
5325 OffsetRect(rect, menu->items_rect.left, menu->items_rect.top);
5327 /* Popup menu item draws in the client area */
5328 if (menu->wFlags & MF_POPUP) MapWindowPoints(hwnd, 0, (POINT *)rect, 2);
5329 else
5331 /* Sysmenu draws in the non-client area */
5332 GetWindowRect(hwnd, &window_rect);
5333 OffsetRect(rect, window_rect.left, window_rect.top);
5336 release_menu_ptr(menu);
5337 return TRUE;
5340 /**********************************************************************
5341 * SetMenuInfo (USER32.@)
5343 * FIXME
5344 * actually use the items to draw the menu
5345 * (recalculate and/or redraw)
5347 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5349 POPUPMENU *menu;
5350 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5352 if (lpmi->fMask & MIM_BACKGROUND)
5353 menu->hbrBack = lpmi->hbrBack;
5355 if (lpmi->fMask & MIM_HELPID)
5356 menu->dwContextHelpID = lpmi->dwContextHelpID;
5358 if (lpmi->fMask & MIM_MAXHEIGHT)
5359 menu->cyMax = lpmi->cyMax;
5361 if (lpmi->fMask & MIM_MENUDATA)
5362 menu->dwMenuData = lpmi->dwMenuData;
5364 if (lpmi->fMask & MIM_STYLE)
5365 menu->dwStyle = lpmi->dwStyle;
5367 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5368 int i;
5369 MENUITEM *item = menu->items;
5370 for( i = menu->nItems; i; i--, item++)
5371 if( item->fType & MF_POPUP)
5372 menu_SetMenuInfo( item->hSubMenu, lpmi);
5374 return TRUE;
5377 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5379 TRACE("(%p %p)\n", hMenu, lpmi);
5380 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5381 if( lpmi->fMask & MIM_STYLE) {
5382 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5383 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5384 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5386 return TRUE;
5388 SetLastError( ERROR_INVALID_PARAMETER);
5389 return FALSE;
5392 /**********************************************************************
5393 * GetMenuInfo (USER32.@)
5395 * NOTES
5396 * win98/NT5.0
5399 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5401 POPUPMENU *menu;
5403 TRACE("(%p %p)\n", hMenu, lpmi);
5405 if (lpmi && (lpmi->cbSize == sizeof(MENUINFO)) && (menu = grab_menu_ptr(hMenu)))
5407 if (lpmi->fMask & MIM_BACKGROUND)
5408 lpmi->hbrBack = menu->hbrBack;
5410 if (lpmi->fMask & MIM_HELPID)
5411 lpmi->dwContextHelpID = menu->dwContextHelpID;
5413 if (lpmi->fMask & MIM_MAXHEIGHT)
5414 lpmi->cyMax = menu->cyMax;
5416 if (lpmi->fMask & MIM_MENUDATA)
5417 lpmi->dwMenuData = menu->dwMenuData;
5419 if (lpmi->fMask & MIM_STYLE)
5420 lpmi->dwStyle = menu->dwStyle;
5422 release_menu_ptr(menu);
5423 return TRUE;
5425 SetLastError( ERROR_INVALID_PARAMETER);
5426 return FALSE;
5430 /**********************************************************************
5431 * SetMenuContextHelpId (USER32.@)
5433 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5435 POPUPMENU *menu;
5437 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5439 if ((menu = grab_menu_ptr(hMenu)))
5441 menu->dwContextHelpID = dwContextHelpID;
5442 release_menu_ptr(menu);
5443 return TRUE;
5445 return FALSE;
5449 /**********************************************************************
5450 * GetMenuContextHelpId (USER32.@)
5452 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5454 DWORD help_id = 0;
5455 POPUPMENU *menu;
5457 TRACE("(%p)\n", hMenu);
5459 if ((menu = grab_menu_ptr(hMenu)))
5461 help_id = menu->dwContextHelpID;
5462 release_menu_ptr(menu);
5465 return help_id;
5468 /**********************************************************************
5469 * MenuItemFromPoint (USER32.@)
5471 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5473 POPUPMENU *menu = grab_menu_ptr(hMenu);
5474 UINT pos;
5476 /*FIXME: Do we have to handle hWnd here? */
5477 if (!menu) return -1;
5479 if (MENU_FindItemByCoords( menu, ptScreen, &pos ) != ht_item)
5480 pos = -1;
5482 release_menu_ptr(menu);
5483 return pos;
5487 /**********************************************************************
5488 * CalcMenuBar (USER32.@)
5490 DWORD WINAPI CalcMenuBar(HWND hwnd, DWORD left, DWORD right, DWORD top, RECT *rect)
5492 FIXME("(%p, %d, %d, %d, %p): stub\n", hwnd, left, right, top, rect);
5493 return 0;
5497 /**********************************************************************
5498 * translate_accelerator
5500 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5501 BYTE fVirt, WORD key, WORD cmd )
5503 INT mask = 0;
5504 UINT mesg = 0;
5506 if (wParam != key) return FALSE;
5508 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5509 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5510 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5512 if (message == WM_CHAR || message == WM_SYSCHAR)
5514 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5516 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5517 goto found;
5520 else
5522 if(fVirt & FVIRTKEY)
5524 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5525 wParam, 0xff & HIWORD(lParam));
5527 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5528 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5530 else
5532 if (!(lParam & 0x01000000)) /* no special_key */
5534 if ((fVirt & FALT) && (lParam & 0x20000000))
5535 { /* ^^ ALT pressed */
5536 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5537 goto found;
5542 return FALSE;
5544 found:
5545 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5546 mesg = 1;
5547 else
5549 HMENU hMenu, hSubMenu, hSysMenu;
5550 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5551 POPUPMENU *menu;
5553 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5554 hSysMenu = get_win_sys_menu( hWnd );
5556 /* find menu item and ask application to initialize it */
5557 /* 1. in the system menu */
5558 if ((menu = find_menu_item(hSysMenu, cmd, MF_BYCOMMAND, NULL)))
5560 hSubMenu = menu->obj.handle;
5561 release_menu_ptr(menu);
5563 if (GetCapture())
5564 mesg = 2;
5565 if (!IsWindowEnabled(hWnd))
5566 mesg = 3;
5567 else
5569 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5570 if(hSubMenu != hSysMenu)
5572 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5573 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5574 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5576 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5579 else /* 2. in the window's menu */
5581 if ((menu = find_menu_item(hMenu, cmd, MF_BYCOMMAND, NULL)))
5583 hSubMenu = menu->obj.handle;
5584 release_menu_ptr(menu);
5586 if (GetCapture())
5587 mesg = 2;
5588 if (!IsWindowEnabled(hWnd))
5589 mesg = 3;
5590 else
5592 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5593 if(hSubMenu != hMenu)
5595 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5596 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5597 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5599 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5604 if (mesg == 0)
5606 if (uSysStat != (UINT)-1)
5608 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5609 mesg=4;
5610 else
5611 mesg=WM_SYSCOMMAND;
5613 else
5615 if (uStat != (UINT)-1)
5617 if (IsIconic(hWnd))
5618 mesg=5;
5619 else
5621 if (uStat & (MF_DISABLED|MF_GRAYED))
5622 mesg=6;
5623 else
5624 mesg=WM_COMMAND;
5627 else
5628 mesg=WM_COMMAND;
5633 if( mesg==WM_COMMAND )
5635 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5636 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5638 else if( mesg==WM_SYSCOMMAND )
5640 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5641 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5643 else
5645 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5646 * #0: unknown (please report!)
5647 * #1: for WM_KEYUP,WM_SYSKEYUP
5648 * #2: mouse is captured
5649 * #3: window is disabled
5650 * #4: it's a disabled system menu option
5651 * #5: it's a menu option, but window is iconic
5652 * #6: it's a menu option, but disabled
5654 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5655 if(mesg==0)
5656 ERR_(accel)(" unknown reason - please report!\n");
5658 return TRUE;
5661 /**********************************************************************
5662 * TranslateAcceleratorA (USER32.@)
5663 * TranslateAccelerator (USER32.@)
5665 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5667 switch (msg->message)
5669 case WM_KEYDOWN:
5670 case WM_SYSKEYDOWN:
5671 return TranslateAcceleratorW( hWnd, hAccel, msg );
5673 case WM_CHAR:
5674 case WM_SYSCHAR:
5676 MSG msgW = *msg;
5677 char ch = LOWORD(msg->wParam);
5678 WCHAR wch;
5679 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5680 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5681 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5684 default:
5685 return 0;
5689 /**********************************************************************
5690 * TranslateAcceleratorW (USER32.@)
5692 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5694 ACCEL data[32], *ptr = data;
5695 int i, count;
5697 if (!hWnd) return 0;
5699 if (msg->message != WM_KEYDOWN &&
5700 msg->message != WM_SYSKEYDOWN &&
5701 msg->message != WM_CHAR &&
5702 msg->message != WM_SYSCHAR)
5703 return 0;
5705 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5706 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5708 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5709 if (count > ARRAY_SIZE( data ))
5711 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5713 count = CopyAcceleratorTableW( hAccel, ptr, count );
5714 for (i = 0; i < count; i++)
5716 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5717 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5718 break;
5720 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5721 return (i < count);