user32: Simplify CheckMenuRadioItem().
[wine.git] / dlls / user32 / menu.c
blob8c541cc4f913201015933d91ca3872aad1751c56
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 "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
57 #include "win.h"
58 #include "controls.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu);
63 WINE_DECLARE_DEBUG_CHANNEL(accel);
65 /* Menu item structure */
66 typedef struct {
67 /* ----------- MENUITEMINFO Stuff ----------- */
68 UINT fType; /* Item type. */
69 UINT fState; /* Item state. */
70 UINT_PTR wID; /* Item id. */
71 HMENU hSubMenu; /* Pop-up menu. */
72 HBITMAP hCheckBit; /* Bitmap when checked. */
73 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
74 LPWSTR text; /* Item text. */
75 ULONG_PTR dwItemData; /* Application defined. */
76 LPWSTR dwTypeData; /* depends on fMask */
77 HBITMAP hbmpItem; /* bitmap */
78 /* ----------- Wine stuff ----------- */
79 RECT rect; /* Item area (relative to the items_rect).
80 * See MENU_AdjustMenuItemRect(). */
81 UINT xTab; /* X position of text after Tab */
82 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
83 * bitmap */
84 } MENUITEM;
86 /* Popup menu structure */
87 typedef struct {
88 struct user_object obj;
89 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
90 WORD Width; /* Width of the whole menu */
91 WORD Height; /* Height of the whole menu */
92 UINT nItems; /* Number of items in the menu */
93 HWND hWnd; /* Window containing the menu */
94 MENUITEM *items; /* Array of menu items */
95 UINT FocusedItem; /* Currently focused item */
96 HWND hwndOwner; /* window receiving the messages for ownerdraw */
97 BOOL bScrolling; /* Scroll arrows are active */
98 UINT nScrollPos; /* Current scroll position */
99 UINT nTotalHeight; /* Total height of menu items inside menu */
100 RECT items_rect; /* Rectangle within which the items lie. Excludes margins and scroll arrows */
101 LONG refcount;
102 /* ------------ MENUINFO members ------ */
103 DWORD dwStyle; /* Extended menu style */
104 UINT cyMax; /* max height of the whole menu, 0 is screen height */
105 HBRUSH hbrBack; /* brush for menu background */
106 DWORD dwContextHelpID;
107 DWORD dwMenuData; /* application defined value */
108 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
109 WORD textOffset; /* Offset of text when items have both bitmaps and text */
110 } POPUPMENU, *LPPOPUPMENU;
112 /* internal flags for menu tracking */
114 #define TF_ENDMENU 0x10000
115 #define TF_SUSPENDPOPUP 0x20000
116 #define TF_SKIPREMOVE 0x40000
117 #define TF_RCVD_BTN_UP 0x80000
119 typedef struct
121 UINT trackFlags;
122 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
123 HMENU hTopMenu; /* initial menu */
124 HWND hOwnerWnd; /* where notifications are sent */
125 POINT pt;
126 } MTRACKER;
128 #define ITEM_PREV -1
129 #define ITEM_NEXT 1
131 /* Internal MENU_TrackMenu() flags */
132 #define TPM_INTERNAL 0xF0000000
133 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
134 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
136 /* Space between 2 columns */
137 #define MENU_COL_SPACE 4
139 /* Margins for popup menus */
140 #define MENU_MARGIN 3
142 /* maximum allowed depth of any branch in the menu tree.
143 * This value is slightly larger than in windows (25) to
144 * stay on the safe side. */
145 #define MAXMENUDEPTH 30
147 /* (other menu->FocusedItem values give the position of the focused item) */
148 #define NO_SELECTED_ITEM 0xffff
150 #define MENU_ITEM_TYPE(flags) \
151 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
153 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
154 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
155 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
157 #define IS_SYSTEM_MENU(menu) \
158 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
160 #define MENUITEMINFO_TYPE_MASK \
161 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
162 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
163 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
164 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
165 #define STATE_MASK (~TYPE_MASK)
166 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
168 static SIZE menucharsize;
169 static UINT ODitemheight; /* default owner drawn item height */
171 /* Use global popup window because there's no way 2 menus can
172 * be tracked at the same time. */
173 static HWND top_popup;
174 static HMENU top_popup_hmenu;
176 /* Flag set by EndMenu() to force an exit from menu tracking */
177 static BOOL fEndMenu = FALSE;
179 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
181 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
183 static BOOL is_win_menu_disallowed(HWND hwnd)
185 return (GetWindowLongW(hwnd, GWL_STYLE) & (WS_CHILD | WS_POPUP)) == WS_CHILD;
188 /*********************************************************************
189 * menu class descriptor
191 const struct builtin_class_descr MENU_builtin_class =
193 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
194 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
195 WINPROC_MENU, /* proc */
196 sizeof(HMENU), /* extra */
197 IDC_ARROW, /* cursor */
198 (HBRUSH)(COLOR_MENU+1) /* brush */
202 /***********************************************************************
203 * debug_print_menuitem
205 * Print a menuitem in readable form.
208 #define debug_print_menuitem(pre, mp, post) \
209 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
211 #define MENUOUT(text) \
212 TRACE("%s%s", (count++ ? "," : ""), (text))
214 #define MENUFLAG(bit,text) \
215 do { \
216 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
217 } while (0)
219 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
220 const char *postfix)
222 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
223 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
224 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
225 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
226 TRACE("%s ", prefix);
227 if (mp) {
228 UINT flags = mp->fType;
229 TRACE( "{ ID=0x%lx", mp->wID);
230 if ( mp->hSubMenu)
231 TRACE( ", Sub=%p", mp->hSubMenu);
232 if (flags) {
233 int count = 0;
234 TRACE( ", fType=");
235 MENUFLAG( MFT_SEPARATOR, "sep");
236 MENUFLAG( MFT_OWNERDRAW, "own");
237 MENUFLAG( MFT_BITMAP, "bit");
238 MENUFLAG(MF_POPUP, "pop");
239 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240 MENUFLAG(MFT_MENUBREAK, "brk");
241 MENUFLAG(MFT_RADIOCHECK, "radio");
242 MENUFLAG(MFT_RIGHTORDER, "rorder");
243 MENUFLAG(MF_SYSMENU, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
245 if (flags)
246 TRACE( "+0x%x", flags);
248 flags = mp->fState;
249 if (flags) {
250 int count = 0;
251 TRACE( ", State=");
252 MENUFLAG(MFS_GRAYED, "grey");
253 MENUFLAG(MFS_DEFAULT, "default");
254 MENUFLAG(MFS_DISABLED, "dis");
255 MENUFLAG(MFS_CHECKED, "check");
256 MENUFLAG(MFS_HILITE, "hi");
257 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
258 MENUFLAG(MF_MOUSESELECT, "mouse");
259 if (flags)
260 TRACE( "+0x%x", flags);
262 if (mp->hCheckBit)
263 TRACE( ", Chk=%p", mp->hCheckBit);
264 if (mp->hUnCheckBit)
265 TRACE( ", Unc=%p", mp->hUnCheckBit);
266 if (mp->text)
267 TRACE( ", Text=%s", debugstr_w(mp->text));
268 if (mp->dwItemData)
269 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
270 if (mp->hbmpItem)
272 if( IS_MAGIC_BITMAP(mp->hbmpItem))
273 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
274 else
275 TRACE( ", hbitmap=%p", mp->hbmpItem);
277 TRACE( " }");
278 } else
279 TRACE( "NULL");
280 TRACE(" %s\n", postfix);
283 #undef MENUOUT
284 #undef MENUFLAG
287 /***********************************************************************
288 * MENU_GetMenu
290 * Validate the given menu handle and returns the menu structure pointer.
292 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
294 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
296 if (menu == OBJ_OTHER_PROCESS)
298 WARN( "other process menu %p?\n", hMenu);
299 return NULL;
301 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
302 else WARN("invalid menu handle=%p\n", hMenu);
303 return menu;
306 static POPUPMENU *grab_menu_ptr(HMENU hMenu)
308 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
310 if (menu == OBJ_OTHER_PROCESS)
312 WARN("other process menu %p?\n", hMenu);
313 return NULL;
316 if (menu)
317 menu->refcount++;
318 else
319 WARN("invalid menu handle=%p\n", hMenu);
320 return menu;
323 static void release_menu_ptr(POPUPMENU *menu)
325 if (menu)
327 menu->refcount--;
328 release_user_handle_ptr(menu);
332 /***********************************************************************
333 * get_win_sys_menu
335 * Get the system menu of a window
337 static HMENU get_win_sys_menu( HWND hwnd )
339 HMENU ret = 0;
340 WND *win = WIN_GetPtr( hwnd );
341 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
343 ret = win->hSysMenu;
344 WIN_ReleasePtr( win );
346 return ret;
349 /***********************************************************************
350 * get_menu_font
352 static HFONT get_menu_font( BOOL bold )
354 static HFONT hMenuFont, hMenuFontBold;
356 HFONT ret = bold ? hMenuFontBold : hMenuFont;
358 if (!ret)
360 NONCLIENTMETRICSW ncm;
361 HFONT prev;
363 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
364 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
366 if (bold)
368 ncm.lfMenuFont.lfWeight += 300;
369 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
371 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
372 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
373 ret, NULL );
374 if (prev)
376 /* another thread beat us to it */
377 DeleteObject( ret );
378 ret = prev;
381 return ret;
384 /***********************************************************************
385 * get_arrow_bitmap
387 static HBITMAP get_arrow_bitmap(void)
389 static HBITMAP arrow_bitmap;
391 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
392 return arrow_bitmap;
395 static inline UINT get_scroll_arrow_height(const POPUPMENU *menu)
397 return menucharsize.cy + 4;
400 /***********************************************************************
401 * MENU_CopySysPopup
403 * Return the default system menu.
405 static HMENU MENU_CopySysPopup(BOOL mdi)
407 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
408 static const WCHAR sysmenumdiW[] = {'S','Y','S','M','E','N','U','M','D','I',0};
409 HMENU hMenu = LoadMenuW(user32_module, (mdi ? sysmenumdiW : sysmenuW));
411 if( hMenu ) {
412 MENUINFO minfo;
413 MENUITEMINFOW miteminfo;
414 POPUPMENU* menu = MENU_GetMenu(hMenu);
415 menu->wFlags |= MF_SYSMENU | MF_POPUP;
416 /* decorate the menu with bitmaps */
417 minfo.cbSize = sizeof( MENUINFO);
418 minfo.dwStyle = MNS_CHECKORBMP;
419 minfo.fMask = MIM_STYLE;
420 SetMenuInfo( hMenu, &minfo);
421 miteminfo.cbSize = sizeof( MENUITEMINFOW);
422 miteminfo.fMask = MIIM_BITMAP;
423 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
424 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
425 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
426 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
427 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
428 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
429 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
430 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
431 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
433 else
434 ERR("Unable to load default system menu\n" );
436 TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
438 return hMenu;
442 /**********************************************************************
443 * MENU_GetSysMenu
445 * Create a copy of the system menu. System menu in Windows is
446 * a special menu bar with the single entry - system menu popup.
447 * This popup is presented to the outside world as a "system menu".
448 * However, the real system menu handle is sometimes seen in the
449 * WM_MENUSELECT parameters (and Word 6 likes it this way).
451 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
453 HMENU hMenu;
455 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
456 if ((hMenu = CreateMenu()))
458 POPUPMENU *menu = MENU_GetMenu(hMenu);
459 menu->wFlags = MF_SYSMENU;
460 menu->hWnd = WIN_GetFullHandle( hWnd );
461 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
463 if (!hPopupMenu)
465 if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
466 hPopupMenu = MENU_CopySysPopup(TRUE);
467 else
468 hPopupMenu = MENU_CopySysPopup(FALSE);
471 if (hPopupMenu)
473 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
474 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
476 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
477 (UINT_PTR)hPopupMenu, NULL );
479 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
480 menu->items[0].fState = 0;
481 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
483 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
484 return hMenu;
486 DestroyMenu( hMenu );
488 ERR("failed to load system menu!\n");
489 return 0;
493 /***********************************************************************
494 * MENU_InitSysMenuPopup
496 * Grey the appropriate items in System menu.
498 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
500 BOOL gray;
502 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
503 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
504 gray = ((style & WS_MAXIMIZE) != 0);
505 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
506 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
507 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
508 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
509 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
510 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
511 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
512 gray = (clsStyle & CS_NOCLOSE) != 0;
514 /* The menu item must keep its state if it's disabled */
515 if(gray)
516 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
520 /******************************************************************************
522 * UINT MENU_GetStartOfNextColumn(
523 * HMENU hMenu )
525 *****************************************************************************/
527 static UINT MENU_GetStartOfNextColumn(
528 HMENU hMenu )
530 POPUPMENU *menu = MENU_GetMenu(hMenu);
531 UINT i;
533 if(!menu)
534 return NO_SELECTED_ITEM;
536 i = menu->FocusedItem + 1;
537 if( i == NO_SELECTED_ITEM )
538 return i;
540 for( ; i < menu->nItems; ++i ) {
541 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
542 return i;
545 return NO_SELECTED_ITEM;
549 /******************************************************************************
551 * UINT MENU_GetStartOfPrevColumn(
552 * HMENU hMenu )
554 *****************************************************************************/
556 static UINT MENU_GetStartOfPrevColumn(
557 HMENU hMenu )
559 POPUPMENU *menu = MENU_GetMenu(hMenu);
560 UINT i;
562 if( !menu )
563 return NO_SELECTED_ITEM;
565 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
566 return NO_SELECTED_ITEM;
568 /* Find the start of the column */
570 for(i = menu->FocusedItem; i != 0 &&
571 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
572 --i); /* empty */
574 if(i == 0)
575 return NO_SELECTED_ITEM;
577 for(--i; i != 0; --i) {
578 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
579 break;
582 TRACE("ret %d.\n", i );
584 return i;
587 static POPUPMENU *find_menu_item(HMENU hmenu, UINT id, UINT flags, UINT *pos)
589 UINT fallback_pos = ~0u, i;
590 POPUPMENU *menu;
592 menu = grab_menu_ptr(hmenu);
593 if (!menu)
594 return NULL;
596 if (flags & MF_BYPOSITION)
598 if (id >= menu->nItems)
600 release_menu_ptr(menu);
601 return NULL;
604 if (pos) *pos = id;
605 return menu;
607 else
609 MENUITEM *item = menu->items;
610 for (i = 0; i < menu->nItems; i++, item++)
612 if (item->fType & MF_POPUP)
614 POPUPMENU *submenu = find_menu_item(item->hSubMenu, id, flags, pos);
616 if (submenu)
618 release_menu_ptr(menu);
619 return submenu;
621 else if (item->wID == id)
623 /* fallback to this item if nothing else found */
624 fallback_pos = i;
627 else if (item->wID == id)
629 if (pos) *pos = i;
630 return menu;
635 if (fallback_pos != ~0u)
636 *pos = fallback_pos;
637 else
639 release_menu_ptr(menu);
640 menu = NULL;
643 return menu;
646 /***********************************************************************
647 * MENU_FindSubMenu
649 * Find a Sub menu. Return the position of the submenu, and modifies
650 * *hmenu in case it is found in another sub-menu.
651 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
653 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
655 POPUPMENU *menu;
656 UINT i;
657 MENUITEM *item;
658 if (((*hmenu)==(HMENU)0xffff) ||
659 (!(menu = MENU_GetMenu(*hmenu))))
660 return NO_SELECTED_ITEM;
661 item = menu->items;
662 for (i = 0; i < menu->nItems; i++, item++) {
663 if(!(item->fType & MF_POPUP)) continue;
664 if (item->hSubMenu == hSubTarget) {
665 return i;
667 else {
668 HMENU hsubmenu = item->hSubMenu;
669 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
670 if (pos != NO_SELECTED_ITEM) {
671 *hmenu = hsubmenu;
672 return pos;
676 return NO_SELECTED_ITEM;
679 /***********************************************************************
680 * MENU_FreeItemData
682 static void MENU_FreeItemData( MENUITEM* item )
684 /* delete text */
685 HeapFree( GetProcessHeap(), 0, item->text );
688 /***********************************************************************
689 * MENU_AdjustMenuItemRect
691 * Adjust menu item rectangle according to scrolling state.
693 static void
694 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
696 INT scroll_offset = menu->bScrolling ? menu->nScrollPos : 0;
698 OffsetRect( rect, menu->items_rect.left, menu->items_rect.top - scroll_offset );
701 enum hittest
703 ht_nowhere, /* outside the menu */
704 ht_border, /* anywhere that's not an item or a scroll arrow */
705 ht_item, /* a menu item */
706 ht_scroll_up, /* scroll up arrow */
707 ht_scroll_down /* scroll down arrow */
710 /***********************************************************************
711 * MENU_FindItemByCoords
713 * Find the item at the specified coordinates (screen coords). Does
714 * not work for child windows and therefore should not be called for
715 * an arbitrary system menu.
717 * Returns a hittest code. *pos will contain the position of the
718 * item or NO_SELECTED_ITEM. If the hittest code is ht_scroll_up
719 * or ht_scroll_down then *pos will contain the position of the
720 * item that's just outside the items_rect - ie, the one that would
721 * be scrolled completely into view.
723 static enum hittest MENU_FindItemByCoords( const POPUPMENU *menu, POINT pt, UINT *pos )
725 MENUITEM *item;
726 UINT i;
727 RECT rect;
728 enum hittest ht = ht_border;
730 *pos = NO_SELECTED_ITEM;
732 if (!GetWindowRect(menu->hWnd, &rect) || !PtInRect(&rect, pt))
733 return ht_nowhere;
735 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
736 else pt.x -= rect.left;
737 pt.y -= rect.top;
739 if (!PtInRect(&menu->items_rect, pt))
741 if (!menu->bScrolling || pt.x < menu->items_rect.left || pt.x >= menu->items_rect.right)
742 return ht_border;
744 /* On a scroll arrow. Update pt so that it points to the item just outside items_rect */
745 if (pt.y < menu->items_rect.top)
747 ht = ht_scroll_up;
748 pt.y = menu->items_rect.top - 1;
750 else
752 ht = ht_scroll_down;
753 pt.y = menu->items_rect.bottom;
757 item = menu->items;
758 for (i = 0; i < menu->nItems; i++, item++)
760 rect = item->rect;
761 MENU_AdjustMenuItemRect(menu, &rect);
762 if (PtInRect(&rect, pt))
764 *pos = i;
765 if (ht != ht_scroll_up && ht != ht_scroll_down) ht = ht_item;
766 break;
770 return ht;
774 /***********************************************************************
775 * MENU_FindItemByKey
777 * Find the menu item selected by a key press.
778 * Return item id, -1 if none, -2 if we should close the menu.
780 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
781 WCHAR key, BOOL forceMenuChar )
783 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
785 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
787 if (hmenu)
789 POPUPMENU *menu = MENU_GetMenu( hmenu );
790 MENUITEM *item = menu->items;
791 LRESULT menuchar;
793 if( !forceMenuChar )
795 UINT i;
796 BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
798 for (i = 0; i < menu->nItems; i++, item++)
800 if( item->text)
802 const WCHAR *p = item->text - 2;
805 const WCHAR *q = p + 2;
806 p = strchrW (q, '&');
807 if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */
809 while (p != NULL && p [1] == '&');
810 if (p && (toupperW(p[1]) == toupperW(key))) return i;
814 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
815 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
816 if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
817 if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
819 return (UINT)(-1);
823 /***********************************************************************
824 * MENU_GetBitmapItemSize
826 * Get the size of a bitmap item.
828 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
829 HWND hwndOwner)
831 BITMAP bm;
832 HBITMAP bmp = lpitem->hbmpItem;
834 size->cx = size->cy = 0;
836 /* check if there is a magic menu item associated with this item */
837 switch( (INT_PTR) bmp )
839 case (INT_PTR)HBMMENU_CALLBACK:
841 MEASUREITEMSTRUCT measItem;
842 measItem.CtlType = ODT_MENU;
843 measItem.CtlID = 0;
844 measItem.itemID = lpitem->wID;
845 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
846 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
847 measItem.itemData = lpitem->dwItemData;
848 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
849 size->cx = measItem.itemWidth;
850 size->cy = measItem.itemHeight;
851 return;
853 break;
854 case (INT_PTR)HBMMENU_SYSTEM:
855 if (lpitem->dwItemData)
857 bmp = (HBITMAP)lpitem->dwItemData;
858 break;
860 /* fall through */
861 case (INT_PTR)HBMMENU_MBAR_RESTORE:
862 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
863 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
864 case (INT_PTR)HBMMENU_MBAR_CLOSE:
865 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
866 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
867 size->cy = size->cx;
868 return;
869 case (INT_PTR)HBMMENU_POPUP_CLOSE:
870 case (INT_PTR)HBMMENU_POPUP_RESTORE:
871 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
872 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
873 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
874 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
875 return;
877 if (GetObjectW(bmp, sizeof(bm), &bm ))
879 size->cx = bm.bmWidth;
880 size->cy = bm.bmHeight;
884 /***********************************************************************
885 * MENU_DrawBitmapItem
887 * Draw a bitmap item.
889 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
890 POPUPMENU *menu, HWND hwndOwner, UINT odaction )
892 BITMAP bm;
893 DWORD rop;
894 HDC hdcMem;
895 HBITMAP bmp;
896 int w = rect->right - rect->left;
897 int h = rect->bottom - rect->top;
898 int bmp_xoffset = 0;
899 int left, top;
900 HBITMAP hbmToDraw = lpitem->hbmpItem;
901 bmp = hbmToDraw;
903 /* Check if there is a magic menu item associated with this item */
904 if (IS_MAGIC_BITMAP(hbmToDraw))
906 UINT flags = 0;
907 WCHAR bmchr = 0;
908 RECT r;
910 switch((INT_PTR)hbmToDraw)
912 case (INT_PTR)HBMMENU_SYSTEM:
913 if (lpitem->dwItemData)
915 bmp = (HBITMAP)lpitem->dwItemData;
916 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
918 else
920 static HBITMAP hBmpSysMenu;
922 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
923 bmp = hBmpSysMenu;
924 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
925 /* only use right half of the bitmap */
926 bmp_xoffset = bm.bmWidth / 2;
927 bm.bmWidth -= bmp_xoffset;
929 goto got_bitmap;
930 case (INT_PTR)HBMMENU_MBAR_RESTORE:
931 flags = DFCS_CAPTIONRESTORE;
932 break;
933 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
934 flags = DFCS_CAPTIONMIN;
935 break;
936 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
937 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
938 break;
939 case (INT_PTR)HBMMENU_MBAR_CLOSE:
940 flags = DFCS_CAPTIONCLOSE;
941 break;
942 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
943 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
944 break;
945 case (INT_PTR)HBMMENU_CALLBACK:
947 DRAWITEMSTRUCT drawItem;
948 drawItem.CtlType = ODT_MENU;
949 drawItem.CtlID = 0;
950 drawItem.itemID = lpitem->wID;
951 drawItem.itemAction = odaction;
952 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
953 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
954 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
955 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
956 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
957 drawItem.hwndItem = (HWND)menu->obj.handle;
958 drawItem.hDC = hdc;
959 drawItem.itemData = lpitem->dwItemData;
960 drawItem.rcItem = *rect;
961 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
962 return;
964 break;
965 case (INT_PTR)HBMMENU_POPUP_CLOSE:
966 bmchr = 0x72;
967 break;
968 case (INT_PTR)HBMMENU_POPUP_RESTORE:
969 bmchr = 0x32;
970 break;
971 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
972 bmchr = 0x31;
973 break;
974 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
975 bmchr = 0x30;
976 break;
977 default:
978 FIXME("Magic %p not implemented\n", hbmToDraw);
979 return;
981 if (bmchr)
983 /* draw the magic bitmaps using marlett font characters */
984 /* FIXME: fontsize and the position (x,y) could probably be better */
985 HFONT hfont, hfontsav;
986 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
987 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
988 { 'M','a','r','l','e','t','t',0 } };
989 logfont.lfHeight = min( h, w) - 5 ;
990 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
991 hfont = CreateFontIndirectW( &logfont);
992 hfontsav = SelectObject(hdc, hfont);
993 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
994 SelectObject(hdc, hfontsav);
995 DeleteObject( hfont);
997 else
999 r = *rect;
1000 InflateRect( &r, -1, -1 );
1001 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
1002 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
1004 return;
1007 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
1009 got_bitmap:
1010 hdcMem = CreateCompatibleDC( hdc );
1011 SelectObject( hdcMem, bmp );
1013 /* handle fontsize > bitmap_height */
1014 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
1015 left=rect->left;
1016 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
1017 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
1018 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1019 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
1020 DeleteDC( hdcMem );
1024 /***********************************************************************
1025 * MENU_CalcItemSize
1027 * Calculate the size of the menu item and store it in lpitem->rect.
1029 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1030 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1032 WCHAR *p;
1033 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1034 UINT arrow_bitmap_width;
1035 BITMAP bm;
1036 INT itemheight;
1038 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1039 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1040 (menuBar ? " (MenuBar)" : ""));
1042 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1043 arrow_bitmap_width = bm.bmWidth;
1045 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1046 if( !menucharsize.cx ) {
1047 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1048 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1049 * but it is unlikely an application will depend on that */
1050 ODitemheight = HIWORD( GetDialogBaseUnits());
1053 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1055 if (lpitem->fType & MF_OWNERDRAW)
1057 MEASUREITEMSTRUCT mis;
1058 mis.CtlType = ODT_MENU;
1059 mis.CtlID = 0;
1060 mis.itemID = lpitem->wID;
1061 mis.itemData = lpitem->dwItemData;
1062 mis.itemHeight = ODitemheight;
1063 mis.itemWidth = 0;
1064 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1065 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1066 * width of a menufont character to the width of an owner-drawn menu.
1068 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1069 if (menuBar) {
1070 /* under at least win95 you seem to be given a standard
1071 height for the menu and the height value is ignored */
1072 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1073 } else
1074 lpitem->rect.bottom += mis.itemHeight;
1076 TRACE("id=%04lx size=%dx%d\n",
1077 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1078 lpitem->rect.bottom-lpitem->rect.top);
1079 return;
1082 if (lpitem->fType & MF_SEPARATOR)
1084 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1085 if( !menuBar)
1086 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1087 return;
1090 itemheight = 0;
1091 lpitem->xTab = 0;
1093 if (!menuBar) {
1094 if (lpitem->hbmpItem) {
1095 SIZE size;
1097 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1098 /* Keep the size of the bitmap in callback mode to be able
1099 * to draw it correctly */
1100 lpitem->bmpsize = size;
1101 lppop->textOffset = max( lppop->textOffset, size.cx);
1102 lpitem->rect.right += size.cx + 2;
1103 itemheight = size.cy + 2;
1105 if( !(lppop->dwStyle & MNS_NOCHECK))
1106 lpitem->rect.right += check_bitmap_width;
1107 lpitem->rect.right += 4 + menucharsize.cx;
1108 lpitem->xTab = lpitem->rect.right;
1109 lpitem->rect.right += arrow_bitmap_width;
1110 } else if (lpitem->hbmpItem) { /* menuBar */
1111 SIZE size;
1113 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1114 lpitem->bmpsize = size;
1115 lpitem->rect.right += size.cx;
1116 if( lpitem->text) lpitem->rect.right += 2;
1117 itemheight = size.cy;
1120 /* it must be a text item - unless it's the system menu */
1121 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1122 HFONT hfontOld = NULL;
1123 RECT rc = lpitem->rect;
1124 LONG txtheight, txtwidth;
1126 if ( lpitem->fState & MFS_DEFAULT ) {
1127 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1129 if (menuBar) {
1130 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1131 DT_SINGLELINE|DT_CALCRECT);
1132 lpitem->rect.right += rc.right - rc.left;
1133 itemheight = max( max( itemheight, txtheight),
1134 GetSystemMetrics( SM_CYMENU) - 1);
1135 lpitem->rect.right += 2 * menucharsize.cx;
1136 } else {
1137 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1138 RECT tmprc = rc;
1139 LONG tmpheight;
1140 int n = (int)( p - lpitem->text);
1141 /* Item contains a tab (only meaningful in popup menus) */
1142 /* get text size before the tab */
1143 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1144 DT_SINGLELINE|DT_CALCRECT);
1145 txtwidth = rc.right - rc.left;
1146 p += 1; /* advance past the Tab */
1147 /* get text size after the tab */
1148 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1149 DT_SINGLELINE|DT_CALCRECT);
1150 lpitem->xTab += txtwidth;
1151 txtheight = max( txtheight, tmpheight);
1152 txtwidth += menucharsize.cx + /* space for the tab */
1153 tmprc.right - tmprc.left; /* space for the short cut */
1154 } else {
1155 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1156 DT_SINGLELINE|DT_CALCRECT);
1157 txtwidth = rc.right - rc.left;
1158 lpitem->xTab += txtwidth;
1160 lpitem->rect.right += 2 + txtwidth;
1161 itemheight = max( itemheight,
1162 max( txtheight + 2, menucharsize.cy + 4));
1164 if (hfontOld) SelectObject (hdc, hfontOld);
1165 } else if( menuBar) {
1166 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1168 lpitem->rect.bottom += itemheight;
1169 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1172 /***********************************************************************
1173 * MENU_PopupMenuCalcSize
1175 * Calculate the size of a popup menu.
1177 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, UINT max_height )
1179 MENUITEM *lpitem;
1180 HDC hdc;
1181 UINT start, i;
1182 BOOL textandbmp = FALSE, multi_col = FALSE;
1183 int orgX, orgY, maxTab, maxTabWidth;
1185 lppop->Width = lppop->Height = 0;
1186 SetRectEmpty(&lppop->items_rect);
1188 if (lppop->nItems == 0) return;
1189 hdc = GetDC( 0 );
1191 SelectObject( hdc, get_menu_font(FALSE));
1193 start = 0;
1195 lppop->textOffset = 0;
1197 while (start < lppop->nItems)
1199 lpitem = &lppop->items[start];
1200 orgX = lppop->items_rect.right;
1201 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1202 orgX += MENU_COL_SPACE;
1203 orgY = lppop->items_rect.top;
1205 maxTab = maxTabWidth = 0;
1206 /* Parse items until column break or end of menu */
1207 for (i = start; i < lppop->nItems; i++, lpitem++)
1209 if (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1211 multi_col = TRUE;
1212 if (i != start) break;
1215 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1216 lppop->items_rect.right = max( lppop->items_rect.right, lpitem->rect.right );
1217 orgY = lpitem->rect.bottom;
1218 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1220 maxTab = max( maxTab, lpitem->xTab );
1221 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1223 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1226 /* Finish the column (set all items to the largest width found) */
1227 lppop->items_rect.right = max( lppop->items_rect.right, maxTab + maxTabWidth );
1228 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1230 lpitem->rect.right = lppop->items_rect.right;
1231 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1232 lpitem->xTab = maxTab;
1235 lppop->items_rect.bottom = max( lppop->items_rect.bottom, orgY );
1238 /* if none of the items have both text and bitmap then
1239 * the text and bitmaps are all aligned on the left. If there is at
1240 * least one item with both text and bitmap then bitmaps are
1241 * on the left and texts left aligned with the right hand side
1242 * of the bitmaps */
1243 if( !textandbmp) lppop->textOffset = 0;
1245 lppop->nTotalHeight = lppop->items_rect.bottom;
1247 /* space for the border */
1248 OffsetRect(&lppop->items_rect, MENU_MARGIN, MENU_MARGIN);
1249 lppop->Height = lppop->items_rect.bottom + MENU_MARGIN;
1250 lppop->Width = lppop->items_rect.right + MENU_MARGIN;
1252 /* Adjust popup height if it exceeds maximum */
1253 if (lppop->Height >= max_height)
1255 lppop->Height = max_height;
1256 lppop->bScrolling = !multi_col;
1257 /* When the scroll arrows are present, don't add the top/bottom margin as well */
1258 if (lppop->bScrolling)
1260 lppop->items_rect.top = get_scroll_arrow_height(lppop);
1261 lppop->items_rect.bottom = lppop->Height - get_scroll_arrow_height(lppop);
1264 else
1266 lppop->bScrolling = FALSE;
1269 ReleaseDC( 0, hdc );
1273 /***********************************************************************
1274 * MENU_MenuBarCalcSize
1276 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1277 * height is off by 1 pixel which causes lengthy window relocations when
1278 * active document window is maximized/restored.
1280 * Calculate the size of the menu bar.
1282 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1283 LPPOPUPMENU lppop, HWND hwndOwner )
1285 MENUITEM *lpitem;
1286 UINT start, i, helpPos;
1287 int orgX, orgY;
1289 if ((lprect == NULL) || (lppop == NULL)) return;
1290 if (lppop->nItems == 0) return;
1291 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1292 /* Start with a 1 pixel top border.
1293 This corresponds to the difference between SM_CYMENU and SM_CYMENUSIZE. */
1294 SetRect(&lppop->items_rect, 0, 0, lprect->right - lprect->left, 1);
1295 start = 0;
1296 helpPos = ~0U;
1297 lppop->textOffset = 0;
1298 while (start < lppop->nItems)
1300 lpitem = &lppop->items[start];
1301 orgX = lppop->items_rect.left;
1302 orgY = lppop->items_rect.bottom;
1304 /* Parse items until line break or end of menu */
1305 for (i = start; i < lppop->nItems; i++, lpitem++)
1307 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1308 if ((i != start) &&
1309 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1311 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1312 debug_print_menuitem (" item: ", lpitem, "");
1313 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1315 if (lpitem->rect.right > lppop->items_rect.right)
1317 if (i != start) break;
1318 else lpitem->rect.right = lppop->items_rect.right;
1320 lppop->items_rect.bottom = max( lppop->items_rect.bottom, lpitem->rect.bottom );
1321 orgX = lpitem->rect.right;
1324 /* Finish the line (set all items to the largest height found) */
1325 while (start < i) lppop->items[start++].rect.bottom = lppop->items_rect.bottom;
1328 OffsetRect(&lppop->items_rect, lprect->left, lprect->top);
1329 lppop->Width = lppop->items_rect.right - lppop->items_rect.left;
1330 lppop->Height = lppop->items_rect.bottom - lppop->items_rect.top;
1331 lprect->bottom = lppop->items_rect.bottom;
1333 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1334 /* the last item (if several lines, only move the last line) */
1335 if (helpPos == ~0U) return;
1336 lpitem = &lppop->items[lppop->nItems-1];
1337 orgY = lpitem->rect.top;
1338 orgX = lprect->right - lprect->left;
1339 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1340 if (lpitem->rect.top != orgY) break; /* Other line */
1341 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1342 lpitem->rect.left += orgX - lpitem->rect.right;
1343 lpitem->rect.right = orgX;
1344 orgX = lpitem->rect.left;
1348 static void draw_scroll_arrow(HDC hdc, int x, int top, int height, BOOL up, BOOL enabled)
1350 RECT rect, light_rect;
1351 HBRUSH brush = GetSysColorBrush( enabled ? COLOR_BTNTEXT : COLOR_BTNSHADOW );
1352 HBRUSH light = GetSysColorBrush( COLOR_3DLIGHT );
1354 if (!up)
1356 top = top + height;
1357 if (!enabled)
1359 SetRect( &rect, x + 1, top, x + 2, top + 1);
1360 FillRect( hdc, &rect, light );
1362 top--;
1365 SetRect( &rect, x, top, x + 1, top + 1);
1366 while (height--)
1368 FillRect( hdc, &rect, brush );
1369 if (!enabled && !up && height)
1371 SetRect( &light_rect, rect.right, rect.top, rect.right + 2, rect.bottom );
1372 FillRect( hdc, &light_rect, light );
1374 InflateRect( &rect, 1, 0 );
1375 OffsetRect( &rect, 0, up ? 1 : -1 );
1378 if (!enabled && up)
1380 rect.left += 2;
1381 FillRect( hdc, &rect, light );
1385 /***********************************************************************
1386 * MENU_DrawScrollArrows
1388 * Draw scroll arrows.
1390 static void
1391 MENU_DrawScrollArrows(const POPUPMENU *menu, HDC hdc)
1393 UINT full_height = get_scroll_arrow_height( menu );
1394 UINT arrow_height = full_height / 3;
1395 BOOL at_end = menu->nScrollPos + menu->items_rect.bottom - menu->items_rect.top == menu->nTotalHeight;
1397 draw_scroll_arrow( hdc, menu->Width / 3, arrow_height, arrow_height,
1398 TRUE, menu->nScrollPos != 0);
1399 draw_scroll_arrow( hdc, menu->Width / 3, menu->Height - 2 * arrow_height, arrow_height,
1400 FALSE, !at_end );
1404 /***********************************************************************
1405 * draw_popup_arrow
1407 * Draws the popup-menu arrow.
1409 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1410 UINT arrow_bitmap_height)
1412 HDC hdcMem = CreateCompatibleDC( hdc );
1413 HBITMAP hOrigBitmap;
1415 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1416 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1417 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1418 arrow_bitmap_width, arrow_bitmap_height,
1419 hdcMem, 0, 0, SRCCOPY );
1420 SelectObject( hdcMem, hOrigBitmap );
1421 DeleteDC( hdcMem );
1423 /***********************************************************************
1424 * MENU_DrawMenuItem
1426 * Draw a single menu item.
1428 static void MENU_DrawMenuItem( HWND hwnd, POPUPMENU *menu, HWND hwndOwner, HDC hdc,
1429 MENUITEM *lpitem, BOOL menuBar, UINT odaction )
1431 RECT rect, bmprc;
1432 BOOL flat_menu = FALSE;
1433 int bkgnd;
1434 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1435 HRGN old_clip = NULL, clip;
1437 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1439 if (!menuBar) {
1440 BITMAP bmp;
1441 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1442 arrow_bitmap_width = bmp.bmWidth;
1443 arrow_bitmap_height = bmp.bmHeight;
1446 if (lpitem->fType & MF_SYSMENU)
1448 if( !IsIconic(hwnd) )
1449 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1450 return;
1453 TRACE( "rect=%s\n", wine_dbgstr_rect( &lpitem->rect ) );
1454 rect = lpitem->rect;
1455 MENU_AdjustMenuItemRect( menu, &rect );
1456 if (!IntersectRect( &bmprc, &rect, &menu->items_rect )) /* bmprc is used as a dummy */
1457 return;
1459 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1460 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1462 /* Setup colors */
1464 if (lpitem->fState & MF_HILITE)
1466 if(menuBar && !flat_menu) {
1467 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1468 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1469 } else {
1470 if(lpitem->fState & MF_GRAYED)
1471 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1472 else
1473 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1474 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1477 else
1479 if (lpitem->fState & MF_GRAYED)
1480 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1481 else
1482 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1483 SetBkColor( hdc, GetSysColor( bkgnd ) );
1486 old_clip = CreateRectRgn( 0, 0, 0, 0 );
1487 if (GetClipRgn( hdc, old_clip ) <= 0)
1489 DeleteObject( old_clip );
1490 old_clip = NULL;
1492 clip = CreateRectRgnIndirect( &menu->items_rect );
1493 ExtSelectClipRgn( hdc, clip, RGN_AND );
1494 DeleteObject( clip );
1496 if (lpitem->fType & MF_OWNERDRAW)
1499 ** Experimentation under Windows reveals that an owner-drawn
1500 ** menu is given the rectangle which includes the space it requested
1501 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1502 ** and a popup-menu arrow. This is the value of lpitem->rect.
1503 ** Windows will leave all drawing to the application except for
1504 ** the popup-menu arrow. Windows always draws that itself, after
1505 ** the menu owner has finished drawing.
1507 DRAWITEMSTRUCT dis;
1508 COLORREF old_bk, old_text;
1510 dis.CtlType = ODT_MENU;
1511 dis.CtlID = 0;
1512 dis.itemID = lpitem->wID;
1513 dis.itemData = lpitem->dwItemData;
1514 dis.itemState = 0;
1515 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1516 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1517 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1518 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1519 dis.hwndItem = (HWND)menu->obj.handle;
1520 dis.hDC = hdc;
1521 dis.rcItem = rect;
1522 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1523 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1524 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1525 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1526 old_bk = GetBkColor( hdc );
1527 old_text = GetTextColor( hdc );
1528 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1529 /* Draw the popup-menu arrow */
1530 SetBkColor( hdc, old_bk );
1531 SetTextColor( hdc, old_text );
1532 if (lpitem->fType & MF_POPUP)
1533 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1534 arrow_bitmap_height);
1535 goto done;
1538 if (menuBar && (lpitem->fType & MF_SEPARATOR)) goto done;
1540 if (lpitem->fState & MF_HILITE)
1542 if (flat_menu)
1544 InflateRect (&rect, -1, -1);
1545 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1546 InflateRect (&rect, 1, 1);
1547 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1549 else
1551 if(menuBar)
1552 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1553 else
1554 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1557 else
1558 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1560 SetBkMode( hdc, TRANSPARENT );
1562 /* vertical separator */
1563 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1565 HPEN oldPen;
1566 RECT rc = rect;
1568 rc.left -= MENU_COL_SPACE / 2 + 1;
1569 rc.top = 3;
1570 rc.bottom = menu->Height - 3;
1571 if (flat_menu)
1573 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1574 MoveToEx( hdc, rc.left, rc.top, NULL );
1575 LineTo( hdc, rc.left, rc.bottom );
1576 SelectObject( hdc, oldPen );
1578 else
1579 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1582 /* horizontal separator */
1583 if (lpitem->fType & MF_SEPARATOR)
1585 HPEN oldPen;
1586 RECT rc = rect;
1588 InflateRect( &rc, -1, 0 );
1589 rc.top = ( rc.top + rc.bottom) / 2;
1590 if (flat_menu)
1592 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1593 MoveToEx( hdc, rc.left, rc.top, NULL );
1594 LineTo( hdc, rc.right, rc.top );
1595 SelectObject( hdc, oldPen );
1597 else
1598 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1599 goto done;
1602 /* helper lines for debugging */
1603 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1604 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1605 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1606 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1609 if (lpitem->hbmpItem) {
1610 /* calculate the bitmap rectangle in coordinates relative
1611 * to the item rectangle */
1612 if( menuBar) {
1613 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1614 bmprc.left = 3;
1615 else
1616 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1618 else if (menu->dwStyle & MNS_NOCHECK)
1619 bmprc.left = 4;
1620 else if (menu->dwStyle & MNS_CHECKORBMP)
1621 bmprc.left = 2;
1622 else
1623 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1624 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1625 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1626 bmprc.top = 0;
1627 else
1628 bmprc.top = (rect.bottom - rect.top -
1629 lpitem->bmpsize.cy) / 2;
1630 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1633 if (!menuBar)
1635 HBITMAP bm;
1636 INT y = rect.top + rect.bottom;
1637 BOOL checked = FALSE;
1638 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1639 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1640 /* Draw the check mark
1642 * FIXME:
1643 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1645 if (!(menu->dwStyle & MNS_NOCHECK))
1647 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1648 lpitem->hUnCheckBit;
1649 if (bm) /* we have a custom bitmap */
1651 HDC hdcMem = CreateCompatibleDC( hdc );
1653 SelectObject( hdcMem, bm );
1654 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1655 check_bitmap_width, check_bitmap_height,
1656 hdcMem, 0, 0, SRCCOPY );
1657 DeleteDC( hdcMem );
1658 checked = TRUE;
1660 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1662 RECT r;
1663 HBITMAP bm = CreateBitmap( check_bitmap_width,
1664 check_bitmap_height, 1, 1, NULL );
1665 HDC hdcMem = CreateCompatibleDC( hdc );
1667 SelectObject( hdcMem, bm );
1668 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1669 DrawFrameControl( hdcMem, &r, DFC_MENU,
1670 (lpitem->fType & MFT_RADIOCHECK) ?
1671 DFCS_MENUBULLET : DFCS_MENUCHECK );
1672 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1673 hdcMem, 0, 0, SRCCOPY );
1674 DeleteDC( hdcMem );
1675 DeleteObject( bm );
1676 checked = TRUE;
1679 if (lpitem->hbmpItem && !(checked && (menu->dwStyle & MNS_CHECKORBMP)))
1681 POINT origorg;
1682 /* some applications make this assumption on the DC's origin */
1683 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1684 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, menu, hwndOwner, odaction );
1685 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1687 /* Draw the popup-menu arrow */
1688 if (lpitem->fType & MF_POPUP)
1689 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1690 arrow_bitmap_height);
1691 rect.left += 4;
1692 if( !(menu->dwStyle & MNS_NOCHECK))
1693 rect.left += check_bitmap_width;
1694 rect.right -= arrow_bitmap_width;
1696 else if (lpitem->hbmpItem)
1697 { /* Draw the bitmap */
1698 POINT origorg;
1700 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1701 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, menu, hwndOwner, odaction );
1702 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1704 /* process text if present */
1705 if (lpitem->text)
1707 int i;
1708 HFONT hfontOld = 0;
1710 UINT uFormat = (menuBar) ?
1711 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1712 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1714 if( !(menu->dwStyle & MNS_CHECKORBMP))
1715 rect.left += menu->textOffset;
1717 if ( lpitem->fState & MFS_DEFAULT )
1719 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1722 if (menuBar) {
1723 if( lpitem->hbmpItem)
1724 rect.left += lpitem->bmpsize.cx;
1725 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1726 rect.left += menucharsize.cx;
1727 rect.right -= menucharsize.cx;
1730 for (i = 0; lpitem->text[i]; i++)
1731 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1732 break;
1734 if(lpitem->fState & MF_GRAYED)
1736 if (!(lpitem->fState & MF_HILITE) )
1738 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1739 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1740 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1741 --rect.left; --rect.top; --rect.right; --rect.bottom;
1743 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1746 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1748 /* paint the shortcut text */
1749 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1751 if (lpitem->text[i] == '\t')
1753 rect.left = lpitem->xTab;
1754 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1756 else
1758 rect.right = lpitem->xTab;
1759 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1762 if(lpitem->fState & MF_GRAYED)
1764 if (!(lpitem->fState & MF_HILITE) )
1766 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1767 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1768 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1769 --rect.left; --rect.top; --rect.right; --rect.bottom;
1771 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1773 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1776 if (hfontOld)
1777 SelectObject (hdc, hfontOld);
1780 done:
1781 ExtSelectClipRgn( hdc, old_clip, RGN_COPY );
1782 if (old_clip) DeleteObject( old_clip );
1786 /***********************************************************************
1787 * MENU_DrawPopupMenu
1789 * Paint a popup menu.
1791 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1793 HBRUSH hPrevBrush, brush = GetSysColorBrush( COLOR_MENU );
1794 RECT rect;
1795 POPUPMENU *menu = MENU_GetMenu( hmenu );
1797 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1799 GetClientRect( hwnd, &rect );
1801 if (menu && menu->hbrBack) brush = menu->hbrBack;
1802 if ((hPrevBrush = SelectObject( hdc, brush ))
1803 && SelectObject( hdc, get_menu_font(FALSE) ))
1805 HPEN hPrevPen;
1807 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1809 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1810 if( hPrevPen )
1812 BOOL flat_menu = FALSE;
1814 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1815 if (flat_menu)
1816 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1817 else
1818 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1820 if (menu)
1822 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1823 /* draw menu items */
1824 if (menu->nItems)
1826 MENUITEM *item;
1827 UINT u;
1829 item = menu->items;
1830 for (u = menu->nItems; u > 0; u--, item++)
1831 MENU_DrawMenuItem( hwnd, menu, menu->hwndOwner, hdc,
1832 item, FALSE, ODA_DRAWENTIRE );
1834 /* draw scroll arrows */
1835 if (menu->bScrolling)
1836 MENU_DrawScrollArrows(menu, hdc);
1838 } else
1840 SelectObject( hdc, hPrevBrush );
1845 /***********************************************************************
1846 * MENU_DrawMenuBar
1848 * Paint a menu bar. Returns the height of the menu bar.
1849 * called from [windows/nonclient.c]
1851 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd )
1853 LPPOPUPMENU lppop;
1854 HMENU hMenu = GetMenu(hwnd);
1856 lppop = MENU_GetMenu( hMenu );
1857 if (lppop == NULL || lprect == NULL)
1859 return GetSystemMetrics(SM_CYMENU);
1862 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1866 /***********************************************************************
1867 * MENU_InitPopup
1869 * Popup menu initialization before WM_ENTERMENULOOP.
1871 static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
1873 POPUPMENU *menu;
1874 DWORD ex_style = 0;
1876 TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
1878 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1880 /* store the owner for DrawItem */
1881 if (!IsWindow( hwndOwner ))
1883 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1884 return FALSE;
1886 menu->hwndOwner = hwndOwner;
1888 if (flags & TPM_LAYOUTRTL)
1889 ex_style = WS_EX_LAYOUTRTL;
1891 /* NOTE: In Windows, top menu popup is not owned. */
1892 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1893 WS_POPUP, 0, 0, 0, 0,
1894 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1895 (LPVOID)hmenu );
1896 if( !menu->hWnd ) return FALSE;
1897 return TRUE;
1901 /***********************************************************************
1902 * MENU_ShowPopup
1904 * Display a popup menu.
1906 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1907 INT x, INT y, INT xanchor, INT yanchor )
1909 POPUPMENU *menu;
1910 POINT pt;
1911 HMONITOR monitor;
1912 MONITORINFO info;
1913 UINT max_height;
1915 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1916 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1918 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1919 if (menu->FocusedItem != NO_SELECTED_ITEM)
1921 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1922 menu->FocusedItem = NO_SELECTED_ITEM;
1925 menu->nScrollPos = 0;
1927 /* FIXME: should use item rect */
1928 pt.x = x;
1929 pt.y = y;
1930 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1931 info.cbSize = sizeof(info);
1932 GetMonitorInfoW( monitor, &info );
1934 max_height = info.rcWork.bottom - info.rcWork.top;
1935 if (menu->cyMax)
1936 max_height = min( max_height, menu->cyMax );
1938 MENU_PopupMenuCalcSize( menu, max_height );
1940 /* adjust popup menu pos so that it fits within the desktop */
1942 if (flags & TPM_LAYOUTRTL)
1943 flags ^= TPM_RIGHTALIGN;
1945 if( flags & TPM_RIGHTALIGN ) x -= menu->Width;
1946 if( flags & TPM_CENTERALIGN ) x -= menu->Width / 2;
1948 if( flags & TPM_BOTTOMALIGN ) y -= menu->Height;
1949 if( flags & TPM_VCENTERALIGN ) y -= menu->Height / 2;
1951 if( x + menu->Width > info.rcWork.right)
1953 if( xanchor && x >= menu->Width - xanchor )
1954 x -= menu->Width - xanchor;
1956 if( x + menu->Width > info.rcWork.right)
1957 x = info.rcWork.right - menu->Width;
1959 if( x < info.rcWork.left ) x = info.rcWork.left;
1961 if( y + menu->Height > info.rcWork.bottom)
1963 if( yanchor && y >= menu->Height + yanchor )
1964 y -= menu->Height + yanchor;
1966 if( y + menu->Height > info.rcWork.bottom)
1967 y = info.rcWork.bottom - menu->Height;
1969 if( y < info.rcWork.top ) y = info.rcWork.top;
1971 if (!top_popup) {
1972 top_popup = menu->hWnd;
1973 top_popup_hmenu = hmenu;
1975 /* Display the window */
1977 SetWindowPos( menu->hWnd, HWND_TOPMOST, x, y, menu->Width, menu->Height,
1978 SWP_SHOWWINDOW | SWP_NOACTIVATE );
1979 UpdateWindow( menu->hWnd );
1980 return TRUE;
1984 /***********************************************************************
1985 * MENU_EnsureMenuItemVisible
1987 static void
1988 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1990 if (lppop->bScrolling)
1992 MENUITEM *item = &lppop->items[wIndex];
1993 UINT nOldPos = lppop->nScrollPos;
1994 const RECT *rc = &lppop->items_rect;
1995 UINT scroll_height = rc->bottom - rc->top;
1997 if (item->rect.bottom > lppop->nScrollPos + scroll_height)
1999 lppop->nScrollPos = item->rect.bottom - scroll_height;
2000 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, rc, rc);
2002 else if (item->rect.top < lppop->nScrollPos)
2004 lppop->nScrollPos = item->rect.top;
2005 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, rc, rc);
2008 /* Invalidate the scroll arrows if necessary */
2009 if (nOldPos != lppop->nScrollPos)
2011 RECT arrow_rect = lppop->items_rect;
2012 if (nOldPos == 0 || lppop->nScrollPos == 0)
2014 arrow_rect.top = 0;
2015 arrow_rect.bottom = lppop->items_rect.top;
2016 InvalidateRect(lppop->hWnd, &arrow_rect, FALSE);
2018 if (nOldPos + scroll_height == lppop->nTotalHeight ||
2019 lppop->nScrollPos + scroll_height == lppop->nTotalHeight)
2021 arrow_rect.top = lppop->items_rect.bottom;
2022 arrow_rect.bottom = lppop->Height;
2023 InvalidateRect(lppop->hWnd, &arrow_rect, FALSE);
2030 /***********************************************************************
2031 * MENU_SelectItem
2033 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
2034 BOOL sendMenuSelect, HMENU topmenu )
2036 LPPOPUPMENU lppop;
2037 HDC hdc;
2039 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
2041 lppop = MENU_GetMenu( hmenu );
2042 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
2044 if (lppop->FocusedItem == wIndex) return;
2045 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
2046 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2047 if (!top_popup) {
2048 top_popup = lppop->hWnd;
2049 top_popup_hmenu = hmenu;
2052 SelectObject( hdc, get_menu_font(FALSE));
2054 /* Clear previous highlighted item */
2055 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2057 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2058 MENU_DrawMenuItem( lppop->hWnd, lppop, hwndOwner, hdc, &lppop->items[lppop->FocusedItem],
2059 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2062 /* Highlight new item (if any) */
2063 lppop->FocusedItem = wIndex;
2064 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2066 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2067 lppop->items[wIndex].fState |= MF_HILITE;
2068 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2069 MENU_DrawMenuItem( lppop->hWnd, lppop, hwndOwner, hdc, &lppop->items[wIndex],
2070 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2072 if (sendMenuSelect)
2074 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2075 SendMessageW( hwndOwner, WM_MENUSELECT,
2076 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2077 ip->fType | ip->fState |
2078 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2081 else if (sendMenuSelect) {
2082 if(topmenu){
2083 int pos;
2084 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2085 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2086 MENUITEM *ip = &ptm->items[pos];
2087 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2088 ip->fType | ip->fState |
2089 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2093 ReleaseDC( lppop->hWnd, hdc );
2097 /***********************************************************************
2098 * MENU_MoveSelection
2100 * Moves currently selected item according to the offset parameter.
2101 * If there is no selection then it should select the last item if
2102 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2104 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2106 INT i;
2107 POPUPMENU *menu;
2109 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2111 menu = MENU_GetMenu( hmenu );
2112 if ((!menu) || (!menu->items)) return;
2114 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2116 if( menu->nItems == 1 ) return; else
2117 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2118 ; i += offset)
2119 if (!(menu->items[i].fType & MF_SEPARATOR))
2121 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2122 return;
2126 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2127 i >= 0 && i < menu->nItems ; i += offset)
2128 if (!(menu->items[i].fType & MF_SEPARATOR))
2130 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2131 return;
2136 /**********************************************************************
2137 * insert_menu_item
2139 * Insert (allocate) a new item into a menu.
2141 static POPUPMENU *insert_menu_item(HMENU hMenu, UINT id, UINT flags, UINT *ret_pos)
2143 MENUITEM *newItems;
2144 POPUPMENU *menu;
2145 UINT pos = id;
2147 /* Find where to insert new item */
2148 if (!(menu = find_menu_item(hMenu, id, flags, &pos)))
2150 if (!(menu = grab_menu_ptr(hMenu)))
2151 return NULL;
2152 pos = menu->nItems;
2155 /* Make sure that MDI system buttons stay on the right side.
2156 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2157 * regardless of their id.
2159 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2160 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2161 pos--;
2163 TRACE("inserting at %u flags %x\n", pos, flags);
2165 /* Create new items array */
2167 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2168 if (!newItems)
2170 release_menu_ptr(menu);
2171 WARN("allocation failed\n" );
2172 return NULL;
2174 if (menu->nItems > 0)
2176 /* Copy the old array into the new one */
2177 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2178 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2179 (menu->nItems-pos)*sizeof(MENUITEM) );
2180 HeapFree( GetProcessHeap(), 0, menu->items );
2182 menu->items = newItems;
2183 menu->nItems++;
2184 memset( &newItems[pos], 0, sizeof(*newItems) );
2185 menu->Height = 0; /* force size recalculate */
2187 *ret_pos = pos;
2188 return menu;
2192 /**********************************************************************
2193 * MENU_ParseResource
2195 * Parse a standard menu resource and add items to the menu.
2196 * Return a pointer to the end of the resource.
2198 * NOTE: flags is equivalent to the mtOption field
2200 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2202 WORD flags, id = 0;
2203 LPCWSTR str;
2204 BOOL end_flag;
2208 flags = GET_WORD(res);
2209 end_flag = flags & MF_END;
2210 /* Remove MF_END because it has the same value as MF_HILITE */
2211 flags &= ~MF_END;
2212 res += sizeof(WORD);
2213 if (!(flags & MF_POPUP))
2215 id = GET_WORD(res);
2216 res += sizeof(WORD);
2218 str = (LPCWSTR)res;
2219 res += (strlenW(str) + 1) * sizeof(WCHAR);
2220 if (flags & MF_POPUP)
2222 HMENU hSubMenu = CreatePopupMenu();
2223 if (!hSubMenu) return NULL;
2224 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2225 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2227 else /* Not a popup */
2229 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2231 } while (!end_flag);
2232 return res;
2236 /**********************************************************************
2237 * MENUEX_ParseResource
2239 * Parse an extended menu resource and add items to the menu.
2240 * Return a pointer to the end of the resource.
2242 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2244 WORD resinfo;
2245 do {
2246 MENUITEMINFOW mii;
2248 mii.cbSize = sizeof(mii);
2249 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2250 mii.fType = GET_DWORD(res);
2251 res += sizeof(DWORD);
2252 mii.fState = GET_DWORD(res);
2253 res += sizeof(DWORD);
2254 mii.wID = GET_DWORD(res);
2255 res += sizeof(DWORD);
2256 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2257 res += sizeof(WORD);
2258 /* Align the text on a word boundary. */
2259 res += (~((UINT_PTR)res - 1)) & 1;
2260 mii.dwTypeData = (LPWSTR) res;
2261 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2262 /* Align the following fields on a dword boundary. */
2263 res += (~((UINT_PTR)res - 1)) & 3;
2265 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2266 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2268 if (resinfo & 1) { /* Pop-up? */
2269 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2270 res += sizeof(DWORD);
2271 mii.hSubMenu = CreatePopupMenu();
2272 if (!mii.hSubMenu)
2273 return NULL;
2274 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2275 DestroyMenu(mii.hSubMenu);
2276 return NULL;
2278 mii.fMask |= MIIM_SUBMENU;
2279 mii.fType |= MF_POPUP;
2281 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2283 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2284 mii.wID, mii.fType);
2285 mii.fType |= MF_SEPARATOR;
2287 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2288 } while (!(resinfo & MF_END));
2289 return res;
2293 /***********************************************************************
2294 * MENU_GetSubPopup
2296 * Return the handle of the selected sub-popup menu (if any).
2298 static HMENU MENU_GetSubPopup( HMENU hmenu )
2300 POPUPMENU *menu;
2301 MENUITEM *item;
2303 menu = MENU_GetMenu( hmenu );
2305 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2307 item = &menu->items[menu->FocusedItem];
2308 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2309 return item->hSubMenu;
2310 return 0;
2314 /***********************************************************************
2315 * MENU_HideSubPopups
2317 * Hide the sub-popup menus of this menu.
2319 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2320 BOOL sendMenuSelect, UINT wFlags )
2322 POPUPMENU *menu = MENU_GetMenu( hmenu );
2324 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2326 if (menu && top_popup)
2328 HMENU hsubmenu;
2329 POPUPMENU *submenu;
2330 MENUITEM *item;
2332 if (menu->FocusedItem != NO_SELECTED_ITEM)
2334 item = &menu->items[menu->FocusedItem];
2335 if (!(item->fType & MF_POPUP) ||
2336 !(item->fState & MF_MOUSESELECT)) return;
2337 item->fState &= ~MF_MOUSESELECT;
2338 hsubmenu = item->hSubMenu;
2339 } else return;
2341 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2342 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2343 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2344 DestroyWindow( submenu->hWnd );
2345 submenu->hWnd = 0;
2347 if (!(wFlags & TPM_NONOTIFY))
2348 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2349 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2354 /***********************************************************************
2355 * MENU_ShowSubPopup
2357 * Display the sub-menu of the selected item of this menu.
2358 * Return the handle of the submenu, or hmenu if no submenu to display.
2360 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2361 BOOL selectFirst, UINT wFlags )
2363 RECT rect;
2364 POPUPMENU *menu;
2365 MENUITEM *item;
2366 HDC hdc;
2368 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2370 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2372 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2374 item = &menu->items[menu->FocusedItem];
2375 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2376 return hmenu;
2378 /* message must be sent before using item,
2379 because nearly everything may be changed by the application ! */
2381 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2382 if (!(wFlags & TPM_NONOTIFY))
2383 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2384 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2386 item = &menu->items[menu->FocusedItem];
2387 rect = item->rect;
2389 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2390 if (!(item->fState & MF_HILITE))
2392 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2393 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2395 SelectObject( hdc, get_menu_font(FALSE));
2397 item->fState |= MF_HILITE;
2398 MENU_DrawMenuItem( menu->hWnd, menu, hwndOwner, hdc, item, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2399 ReleaseDC( menu->hWnd, hdc );
2401 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2402 item->rect = rect;
2404 item->fState |= MF_MOUSESELECT;
2406 if (IS_SYSTEM_MENU(menu))
2408 MENU_InitSysMenuPopup(item->hSubMenu,
2409 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2410 GetClassLongW( menu->hWnd, GCL_STYLE));
2412 NC_GetSysPopupPos( menu->hWnd, &rect );
2413 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2414 rect.top = rect.bottom;
2415 rect.right = GetSystemMetrics(SM_CXSIZE);
2416 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2418 else
2420 RECT item_rect = item->rect;
2422 MENU_AdjustMenuItemRect(menu, &item_rect);
2423 GetWindowRect( menu->hWnd, &rect );
2425 if (menu->wFlags & MF_POPUP)
2427 /* The first item in the popup menu has to be at the
2428 same y position as the focused menu item */
2429 if (wFlags & TPM_LAYOUTRTL)
2430 rect.left += GetSystemMetrics(SM_CXBORDER);
2431 else
2432 rect.left += item_rect.right - GetSystemMetrics(SM_CXBORDER);
2433 rect.top += item_rect.top - MENU_MARGIN;
2434 rect.right = item_rect.left - item_rect.right + GetSystemMetrics(SM_CXBORDER);
2435 rect.bottom = item_rect.top - item_rect.bottom - 2 * MENU_MARGIN;
2437 else
2439 if (wFlags & TPM_LAYOUTRTL)
2440 rect.left = rect.right - item_rect.left;
2441 else
2442 rect.left += item_rect.left;
2443 rect.top += item_rect.bottom;
2444 rect.right = item_rect.right - item_rect.left;
2445 rect.bottom = item_rect.bottom - item_rect.top;
2449 /* use default alignment for submenus */
2450 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2452 MENU_InitPopup( hwndOwner, item->hSubMenu, wFlags );
2454 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2455 rect.left, rect.top, rect.right, rect.bottom );
2456 if (selectFirst)
2457 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2458 return item->hSubMenu;
2463 /**********************************************************************
2464 * MENU_IsMenuActive
2466 HWND MENU_IsMenuActive(void)
2468 return top_popup;
2471 /**********************************************************************
2472 * MENU_EndMenu
2474 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2476 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2478 void MENU_EndMenu( HWND hwnd )
2480 POPUPMENU *menu;
2481 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2482 if (menu && (hwnd == menu->hWnd || hwnd == menu->hwndOwner)) EndMenu();
2485 /***********************************************************************
2486 * MENU_PtMenu
2488 * Walks menu chain trying to find a menu pt maps to.
2490 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2492 POPUPMENU *menu = MENU_GetMenu( hMenu );
2493 UINT item = menu->FocusedItem;
2494 HMENU ret;
2496 /* try subpopup first (if any) */
2497 ret = (item != NO_SELECTED_ITEM &&
2498 (menu->items[item].fType & MF_POPUP) &&
2499 (menu->items[item].fState & MF_MOUSESELECT))
2500 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2502 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2504 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2505 if( menu->wFlags & MF_POPUP )
2507 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2509 else if (ht == HTSYSMENU)
2510 ret = get_win_sys_menu( menu->hWnd );
2511 else if (ht == HTMENU)
2512 ret = GetMenu( menu->hWnd );
2514 return ret;
2517 /***********************************************************************
2518 * MENU_ExecFocusedItem
2520 * Execute a menu item (for instance when user pressed Enter).
2521 * Return the wID of the executed item. Otherwise, -1 indicating
2522 * that no menu item was executed, -2 if a popup is shown;
2523 * Have to receive the flags for the TrackPopupMenu options to avoid
2524 * sending unwanted message.
2527 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2529 MENUITEM *item;
2530 POPUPMENU *menu = MENU_GetMenu( hMenu );
2532 TRACE("%p hmenu=%p\n", pmt, hMenu);
2534 if (!menu || !menu->nItems ||
2535 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2537 item = &menu->items[menu->FocusedItem];
2539 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2541 if (!(item->fType & MF_POPUP))
2543 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2545 /* If TPM_RETURNCMD is set you return the id, but
2546 do not send a message to the owner */
2547 if(!(wFlags & TPM_RETURNCMD))
2549 if( menu->wFlags & MF_SYSMENU )
2550 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2551 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2552 else
2554 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2555 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2557 if (dwStyle & MNS_NOTIFYBYPOS)
2558 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2559 (LPARAM)hMenu);
2560 else
2561 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2564 return item->wID;
2567 else
2569 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2570 return -2;
2573 return -1;
2576 /***********************************************************************
2577 * MENU_SwitchTracking
2579 * Helper function for menu navigation routines.
2581 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2583 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2584 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2586 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2588 if( pmt->hTopMenu != hPtMenu &&
2589 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2591 /* both are top level menus (system and menu-bar) */
2592 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2593 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2594 pmt->hTopMenu = hPtMenu;
2596 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2597 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2601 /***********************************************************************
2602 * MENU_ButtonDown
2604 * Return TRUE if we can go on with menu tracking.
2606 static BOOL MENU_ButtonDown( MTRACKER* pmt, UINT message, HMENU hPtMenu, UINT wFlags )
2608 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2610 if (hPtMenu)
2612 UINT pos;
2613 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2614 enum hittest ht = ht_item;
2616 if( IS_SYSTEM_MENU(ptmenu) )
2618 if (message == WM_LBUTTONDBLCLK) return FALSE;
2619 pos = 0;
2621 else
2622 ht = MENU_FindItemByCoords( ptmenu, pmt->pt, &pos );
2624 if (pos != NO_SELECTED_ITEM)
2626 if (ptmenu->FocusedItem != pos)
2627 MENU_SwitchTracking( pmt, hPtMenu, pos, wFlags );
2629 /* If the popup menu is not already "popped" */
2630 if (!(ptmenu->items[pos].fState & MF_MOUSESELECT))
2631 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2634 /* A click on an item or anywhere on a popup keeps tracking going */
2635 if (ht == ht_item || ((ptmenu->wFlags & MF_POPUP) && ht != ht_nowhere))
2636 return TRUE;
2638 return FALSE;
2641 /***********************************************************************
2642 * MENU_ButtonUp
2644 * Return the value of MENU_ExecFocusedItem if
2645 * the selected item was not a popup. Else open the popup.
2646 * A -1 return value indicates that we go on with menu tracking.
2649 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2651 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2653 if (hPtMenu)
2655 UINT pos;
2656 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2658 if( IS_SYSTEM_MENU(ptmenu) )
2659 pos = 0;
2660 else if (MENU_FindItemByCoords( ptmenu, pmt->pt, &pos ) != ht_item)
2661 pos = NO_SELECTED_ITEM;
2663 if (pos != NO_SELECTED_ITEM && (ptmenu->FocusedItem == pos))
2665 debug_print_menuitem ("FocusedItem: ", &ptmenu->items[pos], "");
2667 if (!(ptmenu->items[pos].fType & MF_POPUP))
2669 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2670 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2671 return executedMenuId;
2674 /* If we are dealing with the menu bar */
2675 /* and this is a click on an already "popped" item: */
2676 /* Stop the menu tracking and close the opened submenus */
2677 if(((pmt->hTopMenu == hPtMenu) || IS_SYSTEM_MENU(ptmenu)) && (pmt->trackFlags & TF_RCVD_BTN_UP))
2678 return 0;
2680 if( GetMenu(ptmenu->hWnd) == hPtMenu || IS_SYSTEM_MENU(ptmenu) )
2682 if (pos == NO_SELECTED_ITEM) return 0;
2683 pmt->trackFlags |= TF_RCVD_BTN_UP;
2686 return -1;
2690 /***********************************************************************
2691 * MENU_MouseMove
2693 * Return TRUE if we can go on with menu tracking.
2695 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2697 UINT id = NO_SELECTED_ITEM;
2698 POPUPMENU *ptmenu = NULL;
2700 if( hPtMenu )
2702 ptmenu = MENU_GetMenu( hPtMenu );
2703 if( IS_SYSTEM_MENU(ptmenu) )
2704 id = 0;
2705 else if (MENU_FindItemByCoords( ptmenu, pmt->pt, &id ) != ht_item)
2706 id = NO_SELECTED_ITEM;
2709 if( id == NO_SELECTED_ITEM )
2711 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2712 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2715 else if( ptmenu->FocusedItem != id )
2717 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2718 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2720 return TRUE;
2724 /***********************************************************************
2725 * MENU_DoNextMenu
2727 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2729 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2731 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2732 BOOL atEnd = FALSE;
2734 /* When skipping left, we need to do something special after the
2735 first menu. */
2736 if (vk == VK_LEFT && menu->FocusedItem == 0)
2738 atEnd = TRUE;
2740 /* When skipping right, for the non-system menu, we need to
2741 handle the last non-special menu item (ie skip any window
2742 icons such as MDI maximize, restore or close) */
2743 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2745 UINT i = menu->FocusedItem + 1;
2746 while (i < menu->nItems) {
2747 if ((menu->items[i].wID >= SC_SIZE &&
2748 menu->items[i].wID <= SC_RESTORE)) {
2749 i++;
2750 } else break;
2752 if (i == menu->nItems) {
2753 atEnd = TRUE;
2756 /* When skipping right, we need to cater for the system menu */
2757 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2759 if (menu->FocusedItem == (menu->nItems - 1)) {
2760 atEnd = TRUE;
2764 if( atEnd )
2766 MDINEXTMENU next_menu;
2767 HMENU hNewMenu;
2768 HWND hNewWnd;
2769 UINT id = 0;
2771 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2772 next_menu.hmenuNext = 0;
2773 next_menu.hwndNext = 0;
2774 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2776 TRACE("%p [%p] -> %p [%p]\n",
2777 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2779 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2781 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2782 hNewWnd = pmt->hOwnerWnd;
2783 if( IS_SYSTEM_MENU(menu) )
2785 /* switch to the menu bar */
2787 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2789 if( vk == VK_LEFT )
2791 menu = MENU_GetMenu( hNewMenu );
2792 id = menu->nItems - 1;
2794 /* Skip backwards over any system predefined icons,
2795 eg. MDI close, restore etc icons */
2796 while ((id > 0) &&
2797 (menu->items[id].wID >= SC_SIZE &&
2798 menu->items[id].wID <= SC_RESTORE)) id--;
2801 else if (style & WS_SYSMENU )
2803 /* switch to the system menu */
2804 hNewMenu = get_win_sys_menu( hNewWnd );
2806 else return FALSE;
2808 else /* application returned a new menu to switch to */
2810 hNewMenu = next_menu.hmenuNext;
2811 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2813 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2815 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2817 if (style & WS_SYSMENU &&
2818 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2820 /* get the real system menu */
2821 hNewMenu = get_win_sys_menu(hNewWnd);
2823 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2825 /* FIXME: Not sure what to do here;
2826 * perhaps try to track hNewMenu as a popup? */
2828 TRACE(" -- got confused.\n");
2829 return FALSE;
2832 else return FALSE;
2835 if( hNewMenu != pmt->hTopMenu )
2837 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2838 FALSE, 0 );
2839 if( pmt->hCurrentMenu != pmt->hTopMenu )
2840 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2843 if( hNewWnd != pmt->hOwnerWnd )
2845 pmt->hOwnerWnd = hNewWnd;
2846 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2849 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2850 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2852 return TRUE;
2854 return FALSE;
2857 /***********************************************************************
2858 * MENU_SuspendPopup
2860 * The idea is not to show the popup if the next input message is
2861 * going to hide it anyway.
2863 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT uMsg )
2865 MSG msg;
2867 msg.hwnd = pmt->hOwnerWnd;
2869 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2870 pmt->trackFlags |= TF_SKIPREMOVE;
2872 switch( uMsg )
2874 case WM_KEYDOWN:
2875 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2876 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2878 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2879 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2880 if( msg.message == WM_KEYDOWN &&
2881 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2883 pmt->trackFlags |= TF_SUSPENDPOPUP;
2884 return TRUE;
2887 break;
2890 /* failures go through this */
2891 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2892 return FALSE;
2895 /***********************************************************************
2896 * MENU_KeyEscape
2898 * Handle a VK_ESCAPE key event in a menu.
2900 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2902 BOOL bEndMenu = TRUE;
2904 if (pmt->hCurrentMenu != pmt->hTopMenu)
2906 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2908 if (menu->wFlags & MF_POPUP)
2910 HMENU hmenutmp, hmenuprev;
2912 hmenuprev = hmenutmp = pmt->hTopMenu;
2914 /* close topmost popup */
2915 while (hmenutmp != pmt->hCurrentMenu)
2917 hmenuprev = hmenutmp;
2918 hmenutmp = MENU_GetSubPopup( hmenuprev );
2921 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2922 pmt->hCurrentMenu = hmenuprev;
2923 bEndMenu = FALSE;
2927 return bEndMenu;
2930 /***********************************************************************
2931 * MENU_KeyLeft
2933 * Handle a VK_LEFT key event in a menu.
2935 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags, UINT msg )
2937 POPUPMENU *menu;
2938 HMENU hmenutmp, hmenuprev;
2939 UINT prevcol;
2941 hmenuprev = hmenutmp = pmt->hTopMenu;
2942 menu = MENU_GetMenu( hmenutmp );
2944 /* Try to move 1 column left (if possible) */
2945 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2946 NO_SELECTED_ITEM ) {
2948 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2949 prevcol, TRUE, 0 );
2950 return;
2953 /* close topmost popup */
2954 while (hmenutmp != pmt->hCurrentMenu)
2956 hmenuprev = hmenutmp;
2957 hmenutmp = MENU_GetSubPopup( hmenuprev );
2960 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2961 pmt->hCurrentMenu = hmenuprev;
2963 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2965 /* move menu bar selection if no more popups are left */
2967 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2968 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2970 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2972 /* A sublevel menu was displayed - display the next one
2973 * unless there is another displacement coming up */
2975 if( !MENU_SuspendPopup( pmt, msg ) )
2976 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2977 pmt->hTopMenu, TRUE, wFlags);
2983 /***********************************************************************
2984 * MENU_KeyRight
2986 * Handle a VK_RIGHT key event in a menu.
2988 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags, UINT msg )
2990 HMENU hmenutmp;
2991 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2992 UINT nextcol;
2994 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2995 pmt->hCurrentMenu,
2996 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2997 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2999 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
3001 /* If already displaying a popup, try to display sub-popup */
3003 hmenutmp = pmt->hCurrentMenu;
3004 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
3006 /* if subpopup was displayed then we are done */
3007 if (hmenutmp != pmt->hCurrentMenu) return;
3010 /* Check to see if there's another column */
3011 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
3012 NO_SELECTED_ITEM ) {
3013 TRACE("Going to %d.\n", nextcol );
3014 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
3015 nextcol, TRUE, 0 );
3016 return;
3019 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
3021 if( pmt->hCurrentMenu != pmt->hTopMenu )
3023 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
3024 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
3025 } else hmenutmp = 0;
3027 /* try to move to the next item */
3028 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
3029 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
3031 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
3032 if( !MENU_SuspendPopup( pmt, msg ) )
3033 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
3034 pmt->hTopMenu, TRUE, wFlags);
3038 static void CALLBACK release_capture( BOOL __normal )
3040 set_capture_window( 0, GUI_INMENUMODE, NULL );
3043 /***********************************************************************
3044 * MENU_TrackMenu
3046 * Menu tracking code.
3048 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
3049 HWND hwnd, const RECT *lprect )
3051 MSG msg;
3052 POPUPMENU *menu;
3053 BOOL fRemove;
3054 INT executedMenuId = -1;
3055 MTRACKER mt;
3056 BOOL enterIdleSent = FALSE;
3057 HWND capture_win;
3059 mt.trackFlags = 0;
3060 mt.hCurrentMenu = hmenu;
3061 mt.hTopMenu = hmenu;
3062 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3063 mt.pt.x = x;
3064 mt.pt.y = y;
3066 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3067 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3069 if (!(menu = MENU_GetMenu( hmenu )))
3071 WARN("Invalid menu handle %p\n", hmenu);
3072 SetLastError(ERROR_INVALID_MENU_HANDLE);
3073 return FALSE;
3076 if (wFlags & TPM_BUTTONDOWN)
3078 /* Get the result in order to start the tracking or not */
3079 fRemove = MENU_ButtonDown( &mt, WM_LBUTTONDOWN, hmenu, wFlags );
3080 fEndMenu = !fRemove;
3083 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3085 /* owner may not be visible when tracking a popup, so use the menu itself */
3086 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3087 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3089 if ((wFlags & TPM_POPUPMENU) && menu->nItems == 0)
3090 return FALSE;
3092 __TRY while (!fEndMenu)
3094 menu = MENU_GetMenu( mt.hCurrentMenu );
3095 if (!menu) /* sometimes happens if I do a window manager close */
3096 break;
3098 /* we have to keep the message in the queue until it's
3099 * clear that menu loop is not over yet. */
3101 for (;;)
3103 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3105 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3106 /* remove the message from the queue */
3107 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3109 else
3111 if (!enterIdleSent)
3113 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3114 enterIdleSent = TRUE;
3115 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3117 WaitMessage();
3121 /* check if EndMenu() tried to cancel us, by posting this message */
3122 if(msg.message == WM_CANCELMODE)
3124 /* we are now out of the loop */
3125 fEndMenu = TRUE;
3127 /* remove the message from the queue */
3128 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3130 /* break out of internal loop, ala ESCAPE */
3131 break;
3134 mt.pt = msg.pt;
3136 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3137 enterIdleSent=FALSE;
3139 fRemove = FALSE;
3140 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3143 * Use the mouse coordinates in lParam instead of those in the MSG
3144 * struct to properly handle synthetic messages. They are already
3145 * in screen coordinates.
3147 mt.pt.x = (short)LOWORD(msg.lParam);
3148 mt.pt.y = (short)HIWORD(msg.lParam);
3150 /* Find a menu for this mouse event */
3151 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3153 switch(msg.message)
3155 /* no WM_NC... messages in captured state */
3157 case WM_RBUTTONDBLCLK:
3158 case WM_RBUTTONDOWN:
3159 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3160 /* fall through */
3161 case WM_LBUTTONDBLCLK:
3162 case WM_LBUTTONDOWN:
3163 /* If the message belongs to the menu, removes it from the queue */
3164 /* Else, end menu tracking */
3165 fRemove = MENU_ButtonDown( &mt, msg.message, hmenu, wFlags );
3166 fEndMenu = !fRemove;
3167 break;
3169 case WM_RBUTTONUP:
3170 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3171 /* fall through */
3172 case WM_LBUTTONUP:
3173 /* Check if a menu was selected by the mouse */
3174 if (hmenu)
3176 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3177 TRACE("executedMenuId %d\n", executedMenuId);
3179 /* End the loop if executedMenuId is an item ID */
3180 /* or if the job was done (executedMenuId = 0). */
3181 fEndMenu = fRemove = (executedMenuId != -1);
3183 /* No menu was selected by the mouse */
3184 /* if the function was called by TrackPopupMenu, continue
3185 with the menu tracking. If not, stop it */
3186 else
3187 fEndMenu = !(wFlags & TPM_POPUPMENU);
3189 break;
3191 case WM_MOUSEMOVE:
3192 /* the selected menu item must be changed every time */
3193 /* the mouse moves. */
3195 if (hmenu)
3196 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3198 } /* switch(msg.message) - mouse */
3200 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3202 fRemove = TRUE; /* Keyboard messages are always removed */
3203 switch(msg.message)
3205 case WM_KEYDOWN:
3206 case WM_SYSKEYDOWN:
3207 switch(msg.wParam)
3209 case VK_MENU:
3210 case VK_F10:
3211 fEndMenu = TRUE;
3212 break;
3214 case VK_HOME:
3215 case VK_END:
3216 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3217 NO_SELECTED_ITEM, FALSE, 0 );
3218 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3219 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3220 break;
3222 case VK_UP:
3223 case VK_DOWN: /* If on menu bar, pull-down the menu */
3225 menu = MENU_GetMenu( mt.hCurrentMenu );
3226 if (!(menu->wFlags & MF_POPUP))
3227 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3228 else /* otherwise try to move selection */
3229 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3230 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3231 break;
3233 case VK_LEFT:
3234 MENU_KeyLeft( &mt, wFlags, msg.message );
3235 break;
3237 case VK_RIGHT:
3238 MENU_KeyRight( &mt, wFlags, msg.message );
3239 break;
3241 case VK_ESCAPE:
3242 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3243 break;
3245 case VK_F1:
3247 HELPINFO hi;
3248 hi.cbSize = sizeof(HELPINFO);
3249 hi.iContextType = HELPINFO_MENUITEM;
3250 if (menu->FocusedItem == NO_SELECTED_ITEM)
3251 hi.iCtrlId = 0;
3252 else
3253 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3254 hi.hItemHandle = hmenu;
3255 hi.dwContextId = menu->dwContextHelpID;
3256 hi.MousePos = msg.pt;
3257 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3258 break;
3261 default:
3262 TranslateMessage( &msg );
3263 break;
3265 break; /* WM_KEYDOWN */
3267 case WM_CHAR:
3268 case WM_SYSCHAR:
3270 UINT pos;
3272 if (msg.wParam == '\r' || msg.wParam == ' ')
3274 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3275 fEndMenu = (executedMenuId != -2);
3277 break;
3280 /* Hack to avoid control chars. */
3281 /* We will find a better way real soon... */
3282 if (msg.wParam < 32) break;
3284 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3285 LOWORD(msg.wParam), FALSE );
3286 if (pos == (UINT)-2) fEndMenu = TRUE;
3287 else if (pos == (UINT)-1) MessageBeep(0);
3288 else
3290 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3291 TRUE, 0 );
3292 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3293 fEndMenu = (executedMenuId != -2);
3296 break;
3297 } /* switch(msg.message) - kbd */
3299 else
3301 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3302 DispatchMessageW( &msg );
3303 continue;
3306 if (!fEndMenu) fRemove = TRUE;
3308 /* finally remove message from the queue */
3310 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3311 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3312 else mt.trackFlags &= ~TF_SKIPREMOVE;
3314 __FINALLY( release_capture )
3316 /* If dropdown is still painted and the close box is clicked on
3317 then the menu will be destroyed as part of the DispatchMessage above.
3318 This will then invalidate the menu handle in mt.hTopMenu. We should
3319 check for this first. */
3320 if( IsMenu( mt.hTopMenu ) )
3322 menu = MENU_GetMenu( mt.hTopMenu );
3324 if( IsWindow( mt.hOwnerWnd ) )
3326 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3328 if (menu && (menu->wFlags & MF_POPUP))
3330 DestroyWindow( menu->hWnd );
3331 menu->hWnd = 0;
3333 if (!(wFlags & TPM_NONOTIFY))
3334 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3335 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3337 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3338 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3342 SetLastError( ERROR_SUCCESS );
3343 /* The return value is only used by TrackPopupMenu */
3344 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3345 if (executedMenuId == -1) executedMenuId = 0;
3346 return executedMenuId;
3349 /***********************************************************************
3350 * MENU_InitTracking
3352 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3354 POPUPMENU *menu;
3356 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3358 HideCaret(0);
3360 if (!(menu = MENU_GetMenu( hMenu ))) return FALSE;
3362 /* This makes the menus of applications built with Delphi work.
3363 * It also enables menus to be displayed in more than one window,
3364 * but there are some bugs left that need to be fixed in this case.
3366 if (!bPopup) menu->hWnd = hWnd;
3367 if (!top_popup)
3369 top_popup = menu->hWnd;
3370 top_popup_hmenu = hMenu;
3373 fEndMenu = FALSE;
3375 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3376 if (!(wFlags & TPM_NONOTIFY))
3377 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3379 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3381 if (!(wFlags & TPM_NONOTIFY))
3383 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3384 /* If an app changed/recreated menu bar entries in WM_INITMENU
3385 * menu sizes will be recalculated once the menu created/shown.
3389 return TRUE;
3392 /***********************************************************************
3393 * MENU_ExitTracking
3395 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3397 TRACE("hwnd=%p\n", hWnd);
3399 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3400 ShowCaret(0);
3401 top_popup = 0;
3402 top_popup_hmenu = NULL;
3403 return TRUE;
3406 /***********************************************************************
3407 * MENU_TrackMouseMenuBar
3409 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3411 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3413 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3414 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3416 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3418 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3419 if (IsMenu(hMenu))
3421 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3423 /* fetch the window menu again, it may have changed */
3424 hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3425 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3426 MENU_ExitTracking(hWnd, FALSE);
3431 /***********************************************************************
3432 * MENU_TrackKbdMenuBar
3434 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3436 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3438 UINT uItem = NO_SELECTED_ITEM;
3439 HMENU hTrackMenu;
3440 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3442 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3444 /* find window that has a menu */
3446 while (is_win_menu_disallowed(hwnd))
3447 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3449 /* check if we have to track a system menu */
3451 hTrackMenu = GetMenu( hwnd );
3452 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3454 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3455 hTrackMenu = get_win_sys_menu( hwnd );
3456 uItem = 0;
3457 wParam |= HTSYSMENU; /* prevent item lookup */
3459 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3461 if (!IsMenu( hTrackMenu )) return;
3463 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3465 /* fetch the window menu again, it may have changed */
3466 hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
3468 if( wChar && wChar != ' ' )
3470 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3471 if ( uItem >= (UINT)(-2) )
3473 if( uItem == (UINT)(-1) ) MessageBeep(0);
3474 /* schedule end of menu tracking */
3475 wFlags |= TF_ENDMENU;
3476 goto track_menu;
3480 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3482 if (!(wParam & HTSYSMENU) || wChar == ' ')
3484 if( uItem == NO_SELECTED_ITEM )
3485 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3486 else
3487 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3490 track_menu:
3491 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3492 MENU_ExitTracking( hwnd, FALSE );
3495 /**********************************************************************
3496 * TrackPopupMenuEx (USER32.@)
3498 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3499 HWND hWnd, LPTPMPARAMS lpTpm )
3501 POPUPMENU *menu;
3502 BOOL ret = FALSE;
3504 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3505 hMenu, wFlags, x, y, hWnd, lpTpm,
3506 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3508 /* Parameter check */
3509 /* FIXME: this check is performed several times, here and in the called
3510 functions. That could be optimized */
3511 if (!(menu = MENU_GetMenu( hMenu )))
3513 SetLastError( ERROR_INVALID_MENU_HANDLE );
3514 return FALSE;
3517 if (IsWindow(menu->hWnd))
3519 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3520 return FALSE;
3523 if (MENU_InitPopup( hWnd, hMenu, wFlags ))
3525 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3527 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3528 if (!(wFlags & TPM_NONOTIFY))
3529 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3531 if (menu->wFlags & MF_SYSMENU)
3532 MENU_InitSysMenuPopup( hMenu, GetWindowLongW( hWnd, GWL_STYLE ),
3533 GetClassLongW( hWnd, GCL_STYLE));
3535 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3536 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3537 lpTpm ? &lpTpm->rcExclude : NULL );
3538 MENU_ExitTracking(hWnd, TRUE);
3540 if (menu->hWnd)
3542 DestroyWindow( menu->hWnd );
3543 menu->hWnd = 0;
3545 if (!(wFlags & TPM_NONOTIFY))
3546 SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
3547 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3549 SetLastError(0);
3552 return ret;
3555 /**********************************************************************
3556 * TrackPopupMenu (USER32.@)
3558 * Like the win32 API, the function return the command ID only if the
3559 * flag TPM_RETURNCMD is on.
3562 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3563 INT nReserved, HWND hWnd, const RECT *lpRect )
3565 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3568 /***********************************************************************
3569 * PopupMenuWndProc
3571 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3573 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3575 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3577 switch(message)
3579 case WM_CREATE:
3581 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3582 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3583 return 0;
3586 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3587 return MA_NOACTIVATE;
3589 case WM_PAINT:
3591 PAINTSTRUCT ps;
3592 BeginPaint( hwnd, &ps );
3593 MENU_DrawPopupMenu( hwnd, ps.hdc,
3594 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3595 EndPaint( hwnd, &ps );
3596 return 0;
3599 case WM_PRINTCLIENT:
3601 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3602 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3603 return 0;
3606 case WM_ERASEBKGND:
3607 return 1;
3609 case WM_DESTROY:
3610 /* zero out global pointer in case resident popup window was destroyed. */
3611 if (hwnd == top_popup) {
3612 top_popup = 0;
3613 top_popup_hmenu = NULL;
3615 break;
3617 case WM_SHOWWINDOW:
3619 if( wParam )
3621 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3623 else
3624 SetWindowLongPtrW( hwnd, 0, 0 );
3625 break;
3627 case MN_GETHMENU:
3628 return GetWindowLongPtrW( hwnd, 0 );
3630 default:
3631 return DefWindowProcW( hwnd, message, wParam, lParam );
3633 return 0;
3637 /***********************************************************************
3638 * MENU_GetMenuBarHeight
3640 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3642 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3643 INT orgX, INT orgY )
3645 HDC hdc;
3646 RECT rectBar;
3647 LPPOPUPMENU lppop;
3649 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3651 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3653 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3654 SelectObject( hdc, get_menu_font(FALSE));
3655 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3656 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3657 ReleaseDC( hwnd, hdc );
3658 return lppop->Height;
3662 /*******************************************************************
3663 * ChangeMenuA (USER32.@)
3665 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3666 UINT id, UINT flags )
3668 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3669 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3670 id, data );
3671 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3672 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3673 id, data );
3674 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3675 flags & MF_BYPOSITION ? pos : id,
3676 flags & ~MF_REMOVE );
3677 /* Default: MF_INSERT */
3678 return InsertMenuA( hMenu, pos, flags, id, data );
3682 /*******************************************************************
3683 * ChangeMenuW (USER32.@)
3685 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3686 UINT id, UINT flags )
3688 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3689 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3690 id, data );
3691 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3692 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3693 id, data );
3694 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3695 flags & MF_BYPOSITION ? pos : id,
3696 flags & ~MF_REMOVE );
3697 /* Default: MF_INSERT */
3698 return InsertMenuW( hMenu, pos, flags, id, data );
3702 /*******************************************************************
3703 * CheckMenuItem (USER32.@)
3705 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3707 POPUPMENU *menu;
3708 MENUITEM *item;
3709 DWORD ret;
3710 UINT pos;
3712 if (!(menu = find_menu_item(hMenu, id, flags, &pos)))
3713 return -1;
3714 item = &menu->items[pos];
3716 ret = item->fState & MF_CHECKED;
3717 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3718 else item->fState &= ~MF_CHECKED;
3719 release_menu_ptr(menu);
3720 return ret;
3724 /**********************************************************************
3725 * EnableMenuItem (USER32.@)
3727 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT id, UINT wFlags )
3729 UINT oldflags, pos;
3730 POPUPMENU *menu;
3731 MENUITEM *item;
3733 TRACE("(%p, %04x, %04x)\n", hMenu, id, wFlags);
3735 /* Get the Popupmenu to access the owner menu */
3736 if (!(menu = find_menu_item(hMenu, id, wFlags, &pos)))
3737 return (UINT)-1;
3739 item = &menu->items[pos];
3740 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3741 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3743 /* If the close item in the system menu change update the close button */
3744 if ((item->wID == SC_CLOSE) && (oldflags != wFlags))
3746 if (menu->hSysMenuOwner)
3748 RECT rc;
3749 POPUPMENU* parentMenu;
3750 HWND hwnd;
3752 /* Get the parent menu to access*/
3753 parentMenu = grab_menu_ptr(menu->hSysMenuOwner);
3754 release_menu_ptr(menu);
3755 if (!parentMenu)
3756 return (UINT)-1;
3758 hwnd = parentMenu->hWnd;
3759 release_menu_ptr(parentMenu);
3761 /* Refresh the frame to reflect the change */
3762 WIN_GetRectangles( hwnd, COORDS_CLIENT, &rc, NULL );
3763 rc.bottom = 0;
3764 RedrawWindow(hwnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3767 else
3768 release_menu_ptr(menu);
3770 return oldflags;
3774 /*******************************************************************
3775 * GetMenuStringA (USER32.@)
3777 INT WINAPI GetMenuStringA(
3778 HMENU hMenu, /* [in] menuhandle */
3779 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3780 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3781 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3782 UINT wFlags /* [in] MF_ flags */
3785 POPUPMENU *menu;
3786 MENUITEM *item;
3787 UINT pos;
3788 INT ret;
3790 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3791 if (str && nMaxSiz) str[0] = '\0';
3793 if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos)))
3795 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3796 return 0;
3798 item = &menu->items[pos];
3800 if (!item->text)
3801 ret = 0;
3802 else if (!str || !nMaxSiz)
3803 ret = WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, 0, NULL, NULL );
3804 else
3806 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3807 str[nMaxSiz-1] = 0;
3808 ret = strlen(str);
3810 release_menu_ptr(menu);
3812 TRACE("returning %s\n", debugstr_a(str));
3813 return ret;
3817 /*******************************************************************
3818 * GetMenuStringW (USER32.@)
3820 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3821 LPWSTR str, INT nMaxSiz, UINT wFlags )
3823 POPUPMENU *menu;
3824 MENUITEM *item;
3825 UINT pos;
3826 INT ret;
3828 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3829 if (str && nMaxSiz) str[0] = '\0';
3831 if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos)))
3833 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3834 return 0;
3836 item = &menu->items[pos];
3838 if (!str || !nMaxSiz)
3839 ret = item->text ? strlenW(item->text) : 0;
3840 else if (!item->text)
3842 str[0] = 0;
3843 ret = 0;
3845 else
3847 lstrcpynW( str, item->text, nMaxSiz );
3848 ret = strlenW(str);
3850 release_menu_ptr(menu);
3852 TRACE("returning %s\n", debugstr_w(str));
3853 return ret;
3857 /**********************************************************************
3858 * HiliteMenuItem (USER32.@)
3860 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3861 UINT wHilite )
3863 POPUPMENU *menu;
3864 UINT pos;
3866 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3868 if (!(menu = find_menu_item(hMenu, wItemID, wHilite, &pos))) return FALSE;
3870 if (menu->FocusedItem != pos)
3872 MENU_HideSubPopups( hWnd, menu->obj.handle, FALSE, 0 );
3873 MENU_SelectItem( hWnd, menu->obj.handle, pos, TRUE, 0 );
3875 release_menu_ptr(menu);
3876 return TRUE;
3880 /**********************************************************************
3881 * GetMenuState (USER32.@)
3883 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3885 POPUPMENU *menu;
3886 UINT state, pos;
3887 MENUITEM *item;
3889 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3891 if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos)))
3892 return -1;
3894 item = &menu->items[pos];
3895 debug_print_menuitem (" item: ", item, "");
3896 if (item->fType & MF_POPUP)
3898 POPUPMENU *submenu = grab_menu_ptr(item->hSubMenu);
3899 if (submenu)
3900 state = (submenu->nItems << 8) | ((item->fState | item->fType) & 0xff);
3901 else
3902 state = -1;
3903 release_menu_ptr(submenu);
3905 else
3907 /* We used to (from way back then) mask the result to 0xff. */
3908 /* I don't know why and it seems wrong as the documented */
3909 /* return flag MF_SEPARATOR is outside that mask. */
3910 state = (item->fType | item->fState);
3912 release_menu_ptr(menu);
3913 return state;
3917 /**********************************************************************
3918 * GetMenuItemCount (USER32.@)
3920 INT WINAPI GetMenuItemCount( HMENU hMenu )
3922 POPUPMENU *menu = grab_menu_ptr(hMenu);
3923 INT count;
3925 if (!menu) return -1;
3926 count = menu->nItems;
3927 release_menu_ptr(menu);
3929 TRACE("(%p) returning %d\n", hMenu, count);
3930 return count;
3934 /**********************************************************************
3935 * GetMenuItemID (USER32.@)
3937 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3939 POPUPMENU *menu;
3940 UINT id, pos;
3942 if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos)))
3943 return -1;
3945 id = menu->items[pos].fType & MF_POPUP ? -1 : menu->items[pos].wID;
3946 release_menu_ptr(menu);
3947 return id;
3951 /**********************************************************************
3952 * MENU_mnu2mnuii
3954 * Uses flags, id and text ptr, passed by InsertMenu() and
3955 * ModifyMenu() to setup a MenuItemInfo structure.
3957 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3958 LPMENUITEMINFOW pmii)
3960 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3961 pmii->cbSize = sizeof( MENUITEMINFOW);
3962 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3963 /* setting bitmap clears text and vice versa */
3964 if( IS_STRING_ITEM(flags)) {
3965 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3966 if( !str)
3967 flags |= MF_SEPARATOR;
3968 /* Item beginning with a backspace is a help item */
3969 /* FIXME: wrong place, this is only true in win16 */
3970 else if( *str == '\b') {
3971 flags |= MF_HELP;
3972 str++;
3974 pmii->dwTypeData = (LPWSTR)str;
3975 } else if( flags & MFT_BITMAP){
3976 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3977 pmii->hbmpItem = (HBITMAP)str;
3979 if( flags & MF_OWNERDRAW){
3980 pmii->fMask |= MIIM_DATA;
3981 pmii->dwItemData = (ULONG_PTR) str;
3983 if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
3984 pmii->fMask |= MIIM_SUBMENU;
3985 pmii->hSubMenu = (HMENU)id;
3987 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3988 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3989 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3990 pmii->wID = (UINT)id;
3994 /*******************************************************************
3995 * InsertMenuW (USER32.@)
3997 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3998 UINT_PTR id, LPCWSTR str )
4000 MENUITEMINFOW mii;
4001 POPUPMENU *menu;
4002 MENUITEM *item;
4003 UINT newpos;
4004 BOOL ret;
4006 if (IS_STRING_ITEM(flags) && str)
4007 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
4008 hMenu, pos, flags, id, debugstr_w(str) );
4009 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
4010 hMenu, pos, flags, id, str );
4012 if (!(menu = insert_menu_item(hMenu, pos, flags, &newpos)))
4013 return FALSE;
4015 MENU_mnu2mnuii( flags, id, str, &mii);
4017 item = &menu->items[newpos];
4018 ret = SetMenuItemInfo_common( item, &mii, TRUE);
4019 if (ret)
4020 item->hCheckBit = item->hUnCheckBit = 0;
4021 else
4022 RemoveMenu( hMenu, pos, flags );
4023 release_menu_ptr(menu);
4025 return ret;
4029 /*******************************************************************
4030 * InsertMenuA (USER32.@)
4032 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
4033 UINT_PTR id, LPCSTR str )
4035 BOOL ret = FALSE;
4037 if (IS_STRING_ITEM(flags) && str)
4039 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4040 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
4041 if (newstr)
4043 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
4044 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
4045 HeapFree( GetProcessHeap(), 0, newstr );
4047 return ret;
4049 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4053 /*******************************************************************
4054 * AppendMenuA (USER32.@)
4056 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
4057 UINT_PTR id, LPCSTR data )
4059 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
4063 /*******************************************************************
4064 * AppendMenuW (USER32.@)
4066 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
4067 UINT_PTR id, LPCWSTR data )
4069 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
4073 /**********************************************************************
4074 * RemoveMenu (USER32.@)
4076 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT id, UINT flags )
4078 POPUPMENU *menu;
4079 UINT pos;
4081 TRACE("(menu=%p id=%#x flags=%04x)\n", hMenu, id, flags);
4083 if (!(menu = find_menu_item(hMenu, id, flags, &pos)))
4084 return FALSE;
4086 /* Remove item */
4087 MENU_FreeItemData( &menu->items[pos] );
4089 if (--menu->nItems == 0)
4091 HeapFree( GetProcessHeap(), 0, menu->items );
4092 menu->items = NULL;
4094 else
4096 MENUITEM *new_items, *item = &menu->items[pos];
4098 while (pos < menu->nItems)
4100 *item = *(item+1);
4101 item++;
4102 pos++;
4104 new_items = HeapReAlloc( GetProcessHeap(), 0, menu->items, menu->nItems * sizeof(MENUITEM) );
4105 if (new_items) menu->items = new_items;
4107 release_menu_ptr(menu);
4109 return TRUE;
4113 /**********************************************************************
4114 * DeleteMenu (USER32.@)
4116 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT id, UINT flags )
4118 POPUPMENU *menu;
4119 UINT pos;
4121 if (!(menu = find_menu_item(hMenu, id, flags, &pos)))
4122 return FALSE;
4124 if (menu->items[pos].fType & MF_POPUP)
4125 DestroyMenu(menu->items[pos].hSubMenu);
4127 RemoveMenu(menu->obj.handle, pos, flags | MF_BYPOSITION);
4128 release_menu_ptr(menu);
4129 return TRUE;
4133 /*******************************************************************
4134 * ModifyMenuW (USER32.@)
4136 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
4137 UINT_PTR id, LPCWSTR str )
4139 MENUITEMINFOW mii;
4140 POPUPMENU *menu;
4141 UINT item_pos;
4142 BOOL ret;
4144 if (IS_STRING_ITEM(flags))
4145 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
4146 else
4147 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
4149 if (!(menu = find_menu_item(hMenu, pos, flags, &item_pos)))
4151 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4152 if (pos == SC_TASKLIST && !(flags & MF_BYPOSITION)) return TRUE;
4153 return FALSE;
4155 menu->Height = 0; /* force size recalculate */
4156 MENU_mnu2mnuii( flags, id, str, &mii);
4157 ret = SetMenuItemInfo_common(&menu->items[item_pos], &mii, TRUE);
4158 release_menu_ptr(menu);
4159 return ret;
4163 /*******************************************************************
4164 * ModifyMenuA (USER32.@)
4166 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
4167 UINT_PTR id, LPCSTR str )
4169 BOOL ret = FALSE;
4171 if (IS_STRING_ITEM(flags) && str)
4173 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4174 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
4175 if (newstr)
4177 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
4178 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
4179 HeapFree( GetProcessHeap(), 0, newstr );
4181 return ret;
4183 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4187 /**********************************************************************
4188 * CreatePopupMenu (USER32.@)
4190 HMENU WINAPI CreatePopupMenu(void)
4192 HMENU hmenu;
4193 POPUPMENU *menu;
4195 if (!(hmenu = CreateMenu())) return 0;
4196 menu = MENU_GetMenu( hmenu );
4197 menu->wFlags |= MF_POPUP;
4198 return hmenu;
4202 /**********************************************************************
4203 * GetMenuCheckMarkDimensions (USER.417)
4204 * GetMenuCheckMarkDimensions (USER32.@)
4206 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4208 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4212 /**********************************************************************
4213 * SetMenuItemBitmaps (USER32.@)
4215 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4216 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4218 POPUPMENU *menu;
4219 MENUITEM *item;
4220 UINT pos;
4222 if (!(menu = find_menu_item(hMenu, nPos, wFlags, &pos)))
4223 return FALSE;
4225 item = &menu->items[pos];
4226 if (!hNewCheck && !hNewUnCheck)
4228 item->fState &= ~MF_USECHECKBITMAPS;
4230 else /* Install new bitmaps */
4232 item->hCheckBit = hNewCheck;
4233 item->hUnCheckBit = hNewUnCheck;
4234 item->fState |= MF_USECHECKBITMAPS;
4236 release_menu_ptr(menu);
4238 return TRUE;
4242 /**********************************************************************
4243 * CreateMenu (USER32.@)
4245 HMENU WINAPI CreateMenu(void)
4247 HMENU hMenu;
4248 LPPOPUPMENU menu;
4250 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4251 menu->FocusedItem = NO_SELECTED_ITEM;
4252 menu->refcount = 1;
4254 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4256 TRACE("return %p\n", hMenu );
4258 return hMenu;
4262 /**********************************************************************
4263 * DestroyMenu (USER32.@)
4265 BOOL WINAPI DestroyMenu( HMENU hMenu )
4267 LPPOPUPMENU lppop;
4269 TRACE("(%p)\n", hMenu);
4271 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4272 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4274 /* DestroyMenu should not destroy system menu popup owner */
4275 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4277 DestroyWindow( lppop->hWnd );
4278 lppop->hWnd = 0;
4281 if (lppop->items) /* recursively destroy submenus */
4283 int i;
4284 MENUITEM *item = lppop->items;
4285 for (i = lppop->nItems; i > 0; i--, item++)
4287 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4288 MENU_FreeItemData( item );
4290 HeapFree( GetProcessHeap(), 0, lppop->items );
4292 HeapFree( GetProcessHeap(), 0, lppop );
4293 return TRUE;
4297 /**********************************************************************
4298 * GetSystemMenu (USER32.@)
4300 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4302 WND *wndPtr = WIN_GetPtr( hWnd );
4303 HMENU retvalue = 0;
4305 if (wndPtr == WND_DESKTOP) return 0;
4306 if (wndPtr == WND_OTHER_PROCESS)
4308 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4310 else if (wndPtr)
4312 if (wndPtr->hSysMenu && bRevert)
4314 DestroyMenu(wndPtr->hSysMenu);
4315 wndPtr->hSysMenu = 0;
4318 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4319 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4321 if( wndPtr->hSysMenu )
4323 POPUPMENU *menu;
4324 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4326 /* Store the dummy sysmenu handle to facilitate the refresh */
4327 /* of the close button if the SC_CLOSE item change */
4328 menu = MENU_GetMenu(retvalue);
4329 if ( menu )
4330 menu->hSysMenuOwner = wndPtr->hSysMenu;
4332 WIN_ReleasePtr( wndPtr );
4334 return bRevert ? 0 : retvalue;
4338 /*******************************************************************
4339 * SetSystemMenu (USER32.@)
4341 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4343 WND *wndPtr = WIN_GetPtr( hwnd );
4345 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4347 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4348 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4349 WIN_ReleasePtr( wndPtr );
4350 return TRUE;
4352 return FALSE;
4356 /**********************************************************************
4357 * GetMenu (USER32.@)
4359 HMENU WINAPI GetMenu( HWND hWnd )
4361 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4362 TRACE("for %p returning %p\n", hWnd, retvalue);
4363 return retvalue;
4366 /**********************************************************************
4367 * GetMenuBarInfo (USER32.@)
4369 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4371 POPUPMENU *menu;
4372 HMENU hmenu = NULL;
4373 ATOM class_atom;
4375 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4377 switch (idObject)
4379 case OBJID_CLIENT:
4380 class_atom = GetClassLongW(hwnd, GCW_ATOM);
4381 if (!class_atom)
4382 return FALSE;
4383 if (class_atom != POPUPMENU_CLASS_ATOM)
4385 WARN("called on invalid window: %d\n", class_atom);
4386 SetLastError(ERROR_INVALID_MENU_HANDLE);
4387 return FALSE;
4390 hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
4391 break;
4392 case OBJID_MENU:
4393 hmenu = GetMenu(hwnd);
4394 break;
4395 case OBJID_SYSMENU:
4396 hmenu = GetSystemMenu(hwnd, FALSE);
4397 break;
4398 default:
4399 return FALSE;
4402 if (!hmenu)
4403 return FALSE;
4405 if (pmbi->cbSize != sizeof(MENUBARINFO))
4407 SetLastError(ERROR_INVALID_PARAMETER);
4408 return FALSE;
4411 menu = MENU_GetMenu(hmenu);
4412 if (!menu)
4413 return FALSE;
4414 if (idItem < 0 || idItem > menu->nItems)
4415 return FALSE;
4417 if (!menu->Height)
4419 SetRectEmpty(&pmbi->rcBar);
4421 else if (idItem == 0)
4423 GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar);
4424 pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
4425 pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
4427 else
4429 GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar);
4432 pmbi->hMenu = hmenu;
4433 pmbi->hwndMenu = NULL;
4434 pmbi->fBarFocused = top_popup_hmenu == hmenu;
4435 if (idItem)
4437 pmbi->fFocused = menu->FocusedItem == idItem - 1;
4438 if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
4440 menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
4441 if (menu)
4442 pmbi->hwndMenu = menu->hWnd;
4445 else
4447 pmbi->fFocused = pmbi->fBarFocused;
4450 return TRUE;
4453 /**********************************************************************
4454 * MENU_SetMenu
4456 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4457 * SetWindowPos call that would result if SetMenu were called directly.
4459 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4461 TRACE("(%p, %p);\n", hWnd, hMenu);
4463 if (hMenu && !IsMenu(hMenu))
4465 WARN("hMenu %p is not a menu handle\n", hMenu);
4466 return FALSE;
4468 if (is_win_menu_disallowed(hWnd))
4469 return FALSE;
4471 hWnd = WIN_GetFullHandle( hWnd );
4472 if (GetCapture() == hWnd)
4473 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4475 if (hMenu)
4477 POPUPMENU *menu;
4479 if (!(menu = grab_menu_ptr(hMenu))) return FALSE;
4480 menu->hWnd = hWnd;
4481 menu->Height = 0; /* Make sure we recalculate the size */
4482 release_menu_ptr(menu);
4484 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4485 return TRUE;
4489 /**********************************************************************
4490 * SetMenu (USER32.@)
4492 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4494 if(!MENU_SetMenu(hWnd, hMenu))
4495 return FALSE;
4497 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4498 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4499 return TRUE;
4503 /**********************************************************************
4504 * GetSubMenu (USER32.@)
4506 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4508 POPUPMENU *menu;
4509 HMENU submenu;
4510 UINT pos;
4512 if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos)))
4513 return 0;
4515 if (menu->items[pos].fType & MF_POPUP)
4516 submenu = menu->items[pos].hSubMenu;
4517 else
4518 submenu = 0;
4520 release_menu_ptr(menu);
4521 return submenu;
4525 /**********************************************************************
4526 * DrawMenuBar (USER32.@)
4528 BOOL WINAPI DrawMenuBar( HWND hWnd )
4530 HMENU hMenu;
4532 if (!IsWindow( hWnd ))
4533 return FALSE;
4534 if (is_win_menu_disallowed(hWnd))
4535 return TRUE;
4537 if ((hMenu = GetMenu( hWnd )))
4539 POPUPMENU *menu = grab_menu_ptr(hMenu);
4540 if (menu)
4542 menu->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4543 menu->hwndOwner = hWnd;
4544 release_menu_ptr(menu);
4548 return SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4549 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4552 /***********************************************************************
4553 * DrawMenuBarTemp (USER32.@)
4555 * UNDOCUMENTED !!
4557 * called by W98SE desk.cpl Control Panel Applet
4559 * Not 100% sure about the param names, but close.
4561 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4563 LPPOPUPMENU lppop;
4564 UINT i,retvalue;
4565 HFONT hfontOld = 0;
4566 BOOL flat_menu = FALSE;
4568 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4570 if (!hMenu)
4571 hMenu = GetMenu(hwnd);
4573 if (!hFont)
4574 hFont = get_menu_font(FALSE);
4576 lppop = MENU_GetMenu( hMenu );
4577 if (lppop == NULL || lprect == NULL)
4579 retvalue = GetSystemMetrics(SM_CYMENU);
4580 goto END;
4583 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4585 hfontOld = SelectObject( hDC, hFont);
4587 if (lppop->Height == 0)
4588 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4590 lprect->bottom = lprect->top + lppop->Height;
4592 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4594 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4595 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4596 LineTo( hDC, lprect->right, lprect->bottom );
4598 if (lppop->nItems == 0)
4600 retvalue = GetSystemMetrics(SM_CYMENU);
4601 goto END;
4604 for (i = 0; i < lppop->nItems; i++)
4605 MENU_DrawMenuItem( hwnd, lppop, hwnd, hDC, &lppop->items[i], TRUE, ODA_DRAWENTIRE );
4607 retvalue = lppop->Height;
4609 END:
4610 if (hfontOld) SelectObject (hDC, hfontOld);
4611 return retvalue;
4614 /***********************************************************************
4615 * EndMenu (USER.187)
4616 * EndMenu (USER32.@)
4618 BOOL WINAPI EndMenu(void)
4620 /* if we are in the menu code, and it is active */
4621 if (!fEndMenu && top_popup)
4623 /* terminate the menu handling code */
4624 fEndMenu = TRUE;
4626 /* needs to be posted to wakeup the internal menu handler */
4627 /* which will now terminate the menu, in the event that */
4628 /* the main window was minimized, or lost focus, so we */
4629 /* don't end up with an orphaned menu */
4630 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4632 return fEndMenu;
4636 /*****************************************************************
4637 * LoadMenuA (USER32.@)
4639 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4641 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4642 if (!hrsrc) return 0;
4643 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4647 /*****************************************************************
4648 * LoadMenuW (USER32.@)
4650 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4652 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4653 if (!hrsrc) return 0;
4654 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4658 /**********************************************************************
4659 * LoadMenuIndirectW (USER32.@)
4661 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4663 HMENU hMenu;
4664 WORD version, offset;
4665 LPCSTR p = template;
4667 version = GET_WORD(p);
4668 p += sizeof(WORD);
4669 TRACE("%p, ver %d\n", template, version );
4670 switch (version)
4672 case 0: /* standard format is version of 0 */
4673 offset = GET_WORD(p);
4674 p += sizeof(WORD) + offset;
4675 if (!(hMenu = CreateMenu())) return 0;
4676 if (!MENU_ParseResource( p, hMenu ))
4678 DestroyMenu( hMenu );
4679 return 0;
4681 return hMenu;
4682 case 1: /* extended format is version of 1 */
4683 offset = GET_WORD(p);
4684 p += sizeof(WORD) + offset;
4685 if (!(hMenu = CreateMenu())) return 0;
4686 if (!MENUEX_ParseResource( p, hMenu))
4688 DestroyMenu( hMenu );
4689 return 0;
4691 return hMenu;
4692 default:
4693 ERR("version %d not supported.\n", version);
4694 return 0;
4699 /**********************************************************************
4700 * LoadMenuIndirectA (USER32.@)
4702 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4704 return LoadMenuIndirectW( template );
4708 /**********************************************************************
4709 * IsMenu (USER32.@)
4711 BOOL WINAPI IsMenu(HMENU hmenu)
4713 POPUPMENU *menu;
4714 BOOL is_menu;
4716 menu = grab_menu_ptr(hmenu);
4717 is_menu = menu != NULL;
4718 release_menu_ptr(menu);
4720 if (!is_menu)
4721 SetLastError(ERROR_INVALID_MENU_HANDLE);
4723 return is_menu;
4726 /**********************************************************************
4727 * GetMenuItemInfo_common
4730 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT id, BOOL bypos,
4731 LPMENUITEMINFOW lpmii, BOOL unicode)
4733 POPUPMENU *menu;
4734 MENUITEM *item;
4735 UINT pos;
4737 menu = find_menu_item(hmenu, id, bypos ? MF_BYPOSITION : 0, &pos);
4739 item = menu ? &menu->items[pos] : NULL;
4741 debug_print_menuitem("GetMenuItemInfo_common: ", item, "");
4743 if (!menu)
4745 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4746 return FALSE;
4749 if( lpmii->fMask & MIIM_TYPE) {
4750 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4751 release_menu_ptr(menu);
4752 WARN("invalid combination of fMask bits used\n");
4753 /* this does not happen on Win9x/ME */
4754 SetLastError( ERROR_INVALID_PARAMETER);
4755 return FALSE;
4757 lpmii->fType = item->fType & MENUITEMINFO_TYPE_MASK;
4758 if (item->hbmpItem && !IS_MAGIC_BITMAP(item->hbmpItem))
4759 lpmii->fType |= MFT_BITMAP;
4760 lpmii->hbmpItem = item->hbmpItem; /* not on Win9x/ME */
4761 if( lpmii->fType & MFT_BITMAP) {
4762 lpmii->dwTypeData = (LPWSTR) item->hbmpItem;
4763 lpmii->cch = 0;
4764 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4765 /* this does not happen on Win9x/ME */
4766 lpmii->dwTypeData = 0;
4767 lpmii->cch = 0;
4771 /* copy the text string */
4772 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4773 if (!item->text) {
4774 if(lpmii->dwTypeData && lpmii->cch) {
4775 if( unicode)
4776 *((WCHAR *)lpmii->dwTypeData) = 0;
4777 else
4778 *((CHAR *)lpmii->dwTypeData) = 0;
4780 lpmii->cch = 0;
4781 } else {
4782 int len;
4783 if (unicode)
4785 len = strlenW(item->text);
4786 if(lpmii->dwTypeData && lpmii->cch)
4787 lstrcpynW(lpmii->dwTypeData, item->text, lpmii->cch);
4789 else
4791 len = WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL,
4792 0, NULL, NULL ) - 1;
4793 if(lpmii->dwTypeData && lpmii->cch)
4794 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1,
4795 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4796 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4798 /* if we've copied a substring we return its length */
4799 if(lpmii->dwTypeData && lpmii->cch)
4800 if (lpmii->cch <= len + 1)
4801 lpmii->cch--;
4802 else
4803 lpmii->cch = len;
4804 else {
4805 /* return length of string */
4806 /* not on Win9x/ME if fType & MFT_BITMAP */
4807 lpmii->cch = len;
4812 if (lpmii->fMask & MIIM_FTYPE)
4813 lpmii->fType = item->fType & MENUITEMINFO_TYPE_MASK;
4815 if (lpmii->fMask & MIIM_BITMAP)
4816 lpmii->hbmpItem = item->hbmpItem;
4818 if (lpmii->fMask & MIIM_STATE)
4819 lpmii->fState = item->fState & MENUITEMINFO_STATE_MASK;
4821 if (lpmii->fMask & MIIM_ID)
4822 lpmii->wID = item->wID;
4824 if (lpmii->fMask & MIIM_SUBMENU)
4825 lpmii->hSubMenu = item->hSubMenu;
4826 else {
4827 /* hSubMenu is always cleared
4828 * (not on Win9x/ME ) */
4829 lpmii->hSubMenu = 0;
4832 if (lpmii->fMask & MIIM_CHECKMARKS) {
4833 lpmii->hbmpChecked = item->hCheckBit;
4834 lpmii->hbmpUnchecked = item->hUnCheckBit;
4836 if (lpmii->fMask & MIIM_DATA)
4837 lpmii->dwItemData = item->dwItemData;
4839 release_menu_ptr(menu);
4840 return TRUE;
4843 /**********************************************************************
4844 * GetMenuItemInfoA (USER32.@)
4846 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4847 LPMENUITEMINFOA lpmii)
4849 BOOL ret;
4850 MENUITEMINFOA mii;
4851 if( lpmii->cbSize != sizeof( mii) &&
4852 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4853 SetLastError( ERROR_INVALID_PARAMETER);
4854 return FALSE;
4856 memcpy( &mii, lpmii, lpmii->cbSize);
4857 mii.cbSize = sizeof( mii);
4858 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4859 (LPMENUITEMINFOW)&mii, FALSE);
4860 mii.cbSize = lpmii->cbSize;
4861 memcpy( lpmii, &mii, mii.cbSize);
4862 return ret;
4865 /**********************************************************************
4866 * GetMenuItemInfoW (USER32.@)
4868 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4869 LPMENUITEMINFOW lpmii)
4871 BOOL ret;
4872 MENUITEMINFOW mii;
4873 if( lpmii->cbSize != sizeof( mii) &&
4874 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4875 SetLastError( ERROR_INVALID_PARAMETER);
4876 return FALSE;
4878 memcpy( &mii, lpmii, lpmii->cbSize);
4879 mii.cbSize = sizeof( mii);
4880 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4881 mii.cbSize = lpmii->cbSize;
4882 memcpy( lpmii, &mii, mii.cbSize);
4883 return ret;
4887 /* set a menu item text from a ASCII or Unicode string */
4888 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4890 if (!text)
4891 menu->text = NULL;
4892 else if (unicode)
4894 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4895 strcpyW( menu->text, text );
4897 else
4899 LPCSTR str = (LPCSTR)text;
4900 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4901 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4902 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4907 /**********************************************************************
4908 * MENU_depth
4910 * detect if there are loops in the menu tree (or the depth is too large)
4912 static int MENU_depth( POPUPMENU *pmenu, int depth)
4914 UINT i;
4915 MENUITEM *item;
4916 int subdepth;
4918 depth++;
4919 if( depth > MAXMENUDEPTH) return depth;
4920 item = pmenu->items;
4921 subdepth = depth;
4922 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4923 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4924 if( psubmenu){
4925 int bdepth = MENU_depth( psubmenu, depth);
4926 if( bdepth > subdepth) subdepth = bdepth;
4928 if( subdepth > MAXMENUDEPTH)
4929 TRACE("<- hmenu %p\n", item->hSubMenu);
4931 return subdepth;
4935 /**********************************************************************
4936 * SetMenuItemInfo_common
4938 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4939 * MIIM_BITMAP and MIIM_STRING flags instead.
4942 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4943 const MENUITEMINFOW *lpmii,
4944 BOOL unicode)
4946 if (!menu) return FALSE;
4948 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4950 if (lpmii->fMask & MIIM_FTYPE ) {
4951 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4952 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4954 if (lpmii->fMask & MIIM_STRING ) {
4955 /* free the string when used */
4956 HeapFree(GetProcessHeap(), 0, menu->text);
4957 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4960 if (lpmii->fMask & MIIM_STATE)
4961 /* Other menu items having MFS_DEFAULT are not converted
4962 to normal items */
4963 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4965 if (lpmii->fMask & MIIM_ID)
4966 menu->wID = lpmii->wID;
4968 if (lpmii->fMask & MIIM_SUBMENU) {
4969 menu->hSubMenu = lpmii->hSubMenu;
4970 if (menu->hSubMenu) {
4971 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4972 if (subMenu) {
4973 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4974 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4975 menu->hSubMenu = 0;
4976 return FALSE;
4978 subMenu->wFlags |= MF_POPUP;
4979 menu->fType |= MF_POPUP;
4980 } else {
4981 SetLastError( ERROR_INVALID_PARAMETER);
4982 return FALSE;
4985 else
4986 menu->fType &= ~MF_POPUP;
4989 if (lpmii->fMask & MIIM_CHECKMARKS)
4991 menu->hCheckBit = lpmii->hbmpChecked;
4992 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4994 if (lpmii->fMask & MIIM_DATA)
4995 menu->dwItemData = lpmii->dwItemData;
4997 if (lpmii->fMask & MIIM_BITMAP)
4998 menu->hbmpItem = lpmii->hbmpItem;
5000 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
5001 menu->fType |= MFT_SEPARATOR;
5003 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
5004 return TRUE;
5007 /**********************************************************************
5008 * MENU_NormalizeMenuItemInfoStruct
5010 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
5011 * check, copy and extend the MENUITEMINFO struct from the version that the application
5012 * supplied to the version used by wine source. */
5013 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
5014 MENUITEMINFOW *pmii_out )
5016 /* do we recognize the size? */
5017 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
5018 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
5019 SetLastError( ERROR_INVALID_PARAMETER);
5020 return FALSE;
5022 /* copy the fields that we have */
5023 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
5024 /* if the hbmpItem member is missing then extend */
5025 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
5026 pmii_out->cbSize = sizeof( MENUITEMINFOW);
5027 pmii_out->hbmpItem = NULL;
5029 /* test for invalid bit combinations */
5030 if( (pmii_out->fMask & MIIM_TYPE &&
5031 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
5032 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
5033 WARN("invalid combination of fMask bits used\n");
5034 /* this does not happen on Win9x/ME */
5035 SetLastError( ERROR_INVALID_PARAMETER);
5036 return FALSE;
5038 /* convert old style (MIIM_TYPE) to the new */
5039 if( pmii_out->fMask & MIIM_TYPE){
5040 pmii_out->fMask |= MIIM_FTYPE;
5041 if( IS_STRING_ITEM(pmii_out->fType)){
5042 pmii_out->fMask |= MIIM_STRING;
5043 } else if( (pmii_out->fType) & MFT_BITMAP){
5044 pmii_out->fMask |= MIIM_BITMAP;
5045 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
5048 return TRUE;
5051 /**********************************************************************
5052 * SetMenuItemInfoA (USER32.@)
5054 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
5055 const MENUITEMINFOA *lpmii)
5057 MENUITEMINFOW mii;
5058 POPUPMENU *menu;
5059 UINT pos;
5060 BOOL ret;
5062 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
5064 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5066 if (!(menu = find_menu_item(hmenu, item, bypos ? MF_BYPOSITION : 0, &pos)))
5068 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
5069 if (item == SC_TASKLIST && !bypos) return TRUE;
5070 return FALSE;
5072 ret = SetMenuItemInfo_common(&menu->items[pos], &mii, FALSE);
5073 release_menu_ptr(menu);
5074 return ret;
5077 /**********************************************************************
5078 * SetMenuItemInfoW (USER32.@)
5080 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
5081 const MENUITEMINFOW *lpmii)
5083 MENUITEMINFOW mii;
5084 POPUPMENU *menu;
5085 BOOL ret;
5086 UINT pos;
5088 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
5090 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
5092 if (!(menu = find_menu_item(hmenu, item, bypos ? MF_BYPOSITION : 0, &pos)))
5094 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
5095 if (item == SC_TASKLIST && !bypos) return TRUE;
5096 return FALSE;
5099 ret = SetMenuItemInfo_common(&menu->items[pos], &mii, TRUE);
5100 release_menu_ptr(menu);
5101 return ret;
5104 static BOOL set_menu_default_item(POPUPMENU *menu, UINT uItem, UINT bypos)
5106 unsigned int i;
5107 MENUITEM *item;
5109 /* reset all default-item flags */
5110 item = menu->items;
5111 for (i = 0; i < menu->nItems; i++, item++)
5113 item->fState &= ~MFS_DEFAULT;
5116 /* no default item */
5117 if (-1 == uItem)
5118 return TRUE;
5120 item = menu->items;
5121 if ( bypos )
5123 if ( uItem >= menu->nItems ) return FALSE;
5124 item[uItem].fState |= MFS_DEFAULT;
5125 return TRUE;
5127 else
5129 for (i = 0; i < menu->nItems; i++, item++)
5131 if (item->wID == uItem)
5133 item->fState |= MFS_DEFAULT;
5134 return TRUE;
5139 return FALSE;
5142 /**********************************************************************
5143 * SetMenuDefaultItem (USER32.@)
5146 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
5148 POPUPMENU *menu;
5149 BOOL ret;
5151 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
5153 if (!(menu = grab_menu_ptr(hmenu))) return FALSE;
5154 ret = set_menu_default_item(menu, uItem, bypos);
5155 release_menu_ptr(menu);
5157 return ret;
5160 /**********************************************************************
5161 * GetMenuDefaultItem (USER32.@)
5163 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
5165 POPUPMENU *menu;
5166 MENUITEM * item;
5167 UINT i = 0;
5169 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
5171 if (!(menu = MENU_GetMenu(hmenu))) return -1;
5173 /* find default item */
5174 item = menu->items;
5176 /* empty menu */
5177 if (! item) return -1;
5179 while ( !( item->fState & MFS_DEFAULT ) )
5181 i++; item++;
5182 if (i >= menu->nItems ) return -1;
5185 /* default: don't return disabled items */
5186 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
5188 /* search rekursiv when needed */
5189 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
5191 UINT ret;
5192 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
5193 if ( -1 != ret ) return ret;
5195 /* when item not found in submenu, return the popup item */
5197 return ( bypos ) ? i : item->wID;
5202 /**********************************************************************
5203 * InsertMenuItemA (USER32.@)
5205 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
5206 const MENUITEMINFOA *lpmii)
5208 MENUITEMINFOW mii;
5209 POPUPMENU *menu;
5210 UINT pos;
5211 BOOL ret;
5213 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
5215 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5217 if (!(menu = insert_menu_item(hMenu, uItem, bypos ? MF_BYPOSITION : 0, &pos)))
5218 return FALSE;
5220 ret = SetMenuItemInfo_common(&menu->items[pos], &mii, FALSE);
5221 release_menu_ptr(menu);
5222 return ret;
5226 /**********************************************************************
5227 * InsertMenuItemW (USER32.@)
5229 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
5230 const MENUITEMINFOW *lpmii)
5232 MENUITEMINFOW mii;
5233 POPUPMENU *menu;
5234 UINT pos;
5235 BOOL ret;
5237 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
5239 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
5241 if (!(menu = insert_menu_item(hMenu, uItem, bypos ? MF_BYPOSITION : 0, &pos)))
5242 return FALSE;
5244 ret = SetMenuItemInfo_common(&menu->items[pos], &mii, TRUE);
5245 release_menu_ptr(menu);
5246 return ret;
5249 /**********************************************************************
5250 * CheckMenuRadioItem (USER32.@)
5253 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu, UINT first, UINT last,
5254 UINT check, UINT flags)
5256 POPUPMENU *first_menu = NULL, *check_menu;
5257 UINT i, check_pos;
5258 BOOL done = FALSE;
5260 for (i = first; i <= last; i++)
5262 MENUITEM *item;
5264 if (!(check_menu = find_menu_item(hMenu, i, flags, &check_pos)))
5265 continue;
5267 if (!first_menu)
5268 first_menu = grab_menu_ptr(check_menu->obj.handle);
5270 if (first_menu != check_menu)
5272 release_menu_ptr(check_menu);
5273 continue;
5276 item = &check_menu->items[check_pos];
5277 if (item->fType != MFT_SEPARATOR)
5279 if (i == check)
5281 item->fType |= MFT_RADIOCHECK;
5282 item->fState |= MFS_CHECKED;
5283 done = TRUE;
5285 else
5287 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5288 item->fState &= ~MFS_CHECKED;
5292 release_menu_ptr(check_menu);
5294 release_menu_ptr(first_menu);
5296 return done;
5300 /**********************************************************************
5301 * GetMenuItemRect (USER32.@)
5303 * ATTENTION: Here, the returned values in rect are the screen
5304 * coordinates of the item just like if the menu was
5305 * always on the upper left side of the application.
5308 BOOL WINAPI GetMenuItemRect(HWND hwnd, HMENU hMenu, UINT uItem, RECT *rect)
5310 POPUPMENU *menu;
5311 UINT pos;
5313 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5315 if (!rect)
5316 return FALSE;
5318 if (!(menu = find_menu_item(hMenu, uItem, MF_BYPOSITION, &pos)))
5319 return FALSE;
5321 if (!hwnd) hwnd = menu->hWnd;
5322 if (!hwnd)
5324 release_menu_ptr(menu);
5325 return FALSE;
5328 *rect = menu->items[pos].rect;
5329 OffsetRect(rect, menu->items_rect.left, menu->items_rect.top);
5330 release_menu_ptr(menu);
5332 MapWindowPoints(hwnd, 0, (POINT *)rect, 2);
5333 return TRUE;
5336 /**********************************************************************
5337 * SetMenuInfo (USER32.@)
5339 * FIXME
5340 * actually use the items to draw the menu
5341 * (recalculate and/or redraw)
5343 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5345 POPUPMENU *menu;
5346 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5348 if (lpmi->fMask & MIM_BACKGROUND)
5349 menu->hbrBack = lpmi->hbrBack;
5351 if (lpmi->fMask & MIM_HELPID)
5352 menu->dwContextHelpID = lpmi->dwContextHelpID;
5354 if (lpmi->fMask & MIM_MAXHEIGHT)
5355 menu->cyMax = lpmi->cyMax;
5357 if (lpmi->fMask & MIM_MENUDATA)
5358 menu->dwMenuData = lpmi->dwMenuData;
5360 if (lpmi->fMask & MIM_STYLE)
5361 menu->dwStyle = lpmi->dwStyle;
5363 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5364 int i;
5365 MENUITEM *item = menu->items;
5366 for( i = menu->nItems; i; i--, item++)
5367 if( item->fType & MF_POPUP)
5368 menu_SetMenuInfo( item->hSubMenu, lpmi);
5370 return TRUE;
5373 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5375 TRACE("(%p %p)\n", hMenu, lpmi);
5376 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5377 if( lpmi->fMask & MIM_STYLE) {
5378 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5379 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5380 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5382 return TRUE;
5384 SetLastError( ERROR_INVALID_PARAMETER);
5385 return FALSE;
5388 /**********************************************************************
5389 * GetMenuInfo (USER32.@)
5391 * NOTES
5392 * win98/NT5.0
5395 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5397 POPUPMENU *menu;
5399 TRACE("(%p %p)\n", hMenu, lpmi);
5401 if (lpmi && (lpmi->cbSize == sizeof(MENUINFO)) && (menu = grab_menu_ptr(hMenu)))
5403 if (lpmi->fMask & MIM_BACKGROUND)
5404 lpmi->hbrBack = menu->hbrBack;
5406 if (lpmi->fMask & MIM_HELPID)
5407 lpmi->dwContextHelpID = menu->dwContextHelpID;
5409 if (lpmi->fMask & MIM_MAXHEIGHT)
5410 lpmi->cyMax = menu->cyMax;
5412 if (lpmi->fMask & MIM_MENUDATA)
5413 lpmi->dwMenuData = menu->dwMenuData;
5415 if (lpmi->fMask & MIM_STYLE)
5416 lpmi->dwStyle = menu->dwStyle;
5418 release_menu_ptr(menu);
5419 return TRUE;
5421 SetLastError( ERROR_INVALID_PARAMETER);
5422 return FALSE;
5426 /**********************************************************************
5427 * SetMenuContextHelpId (USER32.@)
5429 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5431 POPUPMENU *menu;
5433 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5435 if ((menu = grab_menu_ptr(hMenu)))
5437 menu->dwContextHelpID = dwContextHelpID;
5438 release_menu_ptr(menu);
5439 return TRUE;
5441 return FALSE;
5445 /**********************************************************************
5446 * GetMenuContextHelpId (USER32.@)
5448 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5450 DWORD help_id = 0;
5451 POPUPMENU *menu;
5453 TRACE("(%p)\n", hMenu);
5455 if ((menu = grab_menu_ptr(hMenu)))
5457 help_id = menu->dwContextHelpID;
5458 release_menu_ptr(menu);
5461 return help_id;
5464 /**********************************************************************
5465 * MenuItemFromPoint (USER32.@)
5467 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5469 POPUPMENU *menu = grab_menu_ptr(hMenu);
5470 UINT pos;
5472 /*FIXME: Do we have to handle hWnd here? */
5473 if (!menu) return -1;
5475 if (MENU_FindItemByCoords( menu, ptScreen, &pos ) != ht_item)
5476 pos = -1;
5478 release_menu_ptr(menu);
5479 return pos;
5483 /**********************************************************************
5484 * CalcMenuBar (USER32.@)
5486 DWORD WINAPI CalcMenuBar(HWND hwnd, DWORD left, DWORD right, DWORD top, RECT *rect)
5488 FIXME("(%p, %d, %d, %d, %p): stub\n", hwnd, left, right, top, rect);
5489 return 0;
5493 /**********************************************************************
5494 * translate_accelerator
5496 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5497 BYTE fVirt, WORD key, WORD cmd )
5499 INT mask = 0;
5500 UINT mesg = 0;
5502 if (wParam != key) return FALSE;
5504 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5505 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5506 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5508 if (message == WM_CHAR || message == WM_SYSCHAR)
5510 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5512 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5513 goto found;
5516 else
5518 if(fVirt & FVIRTKEY)
5520 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5521 wParam, 0xff & HIWORD(lParam));
5523 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5524 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5526 else
5528 if (!(lParam & 0x01000000)) /* no special_key */
5530 if ((fVirt & FALT) && (lParam & 0x20000000))
5531 { /* ^^ ALT pressed */
5532 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5533 goto found;
5538 return FALSE;
5540 found:
5541 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5542 mesg = 1;
5543 else
5545 HMENU hMenu, hSubMenu, hSysMenu;
5546 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5547 POPUPMENU *menu;
5549 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5550 hSysMenu = get_win_sys_menu( hWnd );
5552 /* find menu item and ask application to initialize it */
5553 /* 1. in the system menu */
5554 if ((menu = find_menu_item(hSysMenu, cmd, MF_BYCOMMAND, NULL)))
5556 hSubMenu = menu->obj.handle;
5557 release_menu_ptr(menu);
5559 if (GetCapture())
5560 mesg = 2;
5561 if (!IsWindowEnabled(hWnd))
5562 mesg = 3;
5563 else
5565 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5566 if(hSubMenu != hSysMenu)
5568 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5569 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5570 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5572 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5575 else /* 2. in the window's menu */
5577 if ((menu = find_menu_item(hMenu, cmd, MF_BYCOMMAND, NULL)))
5579 hSubMenu = menu->obj.handle;
5580 release_menu_ptr(menu);
5582 if (GetCapture())
5583 mesg = 2;
5584 if (!IsWindowEnabled(hWnd))
5585 mesg = 3;
5586 else
5588 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5589 if(hSubMenu != hMenu)
5591 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5592 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5593 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5595 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5600 if (mesg == 0)
5602 if (uSysStat != (UINT)-1)
5604 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5605 mesg=4;
5606 else
5607 mesg=WM_SYSCOMMAND;
5609 else
5611 if (uStat != (UINT)-1)
5613 if (IsIconic(hWnd))
5614 mesg=5;
5615 else
5617 if (uStat & (MF_DISABLED|MF_GRAYED))
5618 mesg=6;
5619 else
5620 mesg=WM_COMMAND;
5623 else
5624 mesg=WM_COMMAND;
5629 if( mesg==WM_COMMAND )
5631 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5632 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5634 else if( mesg==WM_SYSCOMMAND )
5636 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5637 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5639 else
5641 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5642 * #0: unknown (please report!)
5643 * #1: for WM_KEYUP,WM_SYSKEYUP
5644 * #2: mouse is captured
5645 * #3: window is disabled
5646 * #4: it's a disabled system menu option
5647 * #5: it's a menu option, but window is iconic
5648 * #6: it's a menu option, but disabled
5650 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5651 if(mesg==0)
5652 ERR_(accel)(" unknown reason - please report!\n");
5654 return TRUE;
5657 /**********************************************************************
5658 * TranslateAcceleratorA (USER32.@)
5659 * TranslateAccelerator (USER32.@)
5661 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5663 switch (msg->message)
5665 case WM_KEYDOWN:
5666 case WM_SYSKEYDOWN:
5667 return TranslateAcceleratorW( hWnd, hAccel, msg );
5669 case WM_CHAR:
5670 case WM_SYSCHAR:
5672 MSG msgW = *msg;
5673 char ch = LOWORD(msg->wParam);
5674 WCHAR wch;
5675 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5676 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5677 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5680 default:
5681 return 0;
5685 /**********************************************************************
5686 * TranslateAcceleratorW (USER32.@)
5688 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5690 ACCEL data[32], *ptr = data;
5691 int i, count;
5693 if (!hWnd) return 0;
5695 if (msg->message != WM_KEYDOWN &&
5696 msg->message != WM_SYSKEYDOWN &&
5697 msg->message != WM_CHAR &&
5698 msg->message != WM_SYSCHAR)
5699 return 0;
5701 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5702 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5704 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5705 if (count > ARRAY_SIZE( data ))
5707 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5709 count = CopyAcceleratorTableW( hAccel, ptr, count );
5710 for (i = 0; i < count; i++)
5712 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5713 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5714 break;
5716 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5717 return (i < count);