configure: Disable all 16-bit code on Cygwin.
[wine/multimedia.git] / dlls / user32 / menu.c
blobff6af768b1c36953860186d25a68900f00880cf5
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/winbase16.h"
55 #include "wine/winuser16.h"
56 #include "wownt32.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "wine/exception.h"
60 #include "win.h"
61 #include "controls.h"
62 #include "user_private.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(menu);
66 WINE_DECLARE_DEBUG_CHANNEL(accel);
68 /* internal popup menu window messages */
70 #define MM_SETMENUHANDLE (WM_USER + 0)
71 #define MM_GETMENUHANDLE (WM_USER + 1)
73 /* Menu item structure */
74 typedef struct {
75 /* ----------- MENUITEMINFO Stuff ----------- */
76 UINT fType; /* Item type. */
77 UINT fState; /* Item state. */
78 UINT_PTR wID; /* Item id. */
79 HMENU hSubMenu; /* Pop-up menu. */
80 HBITMAP hCheckBit; /* Bitmap when checked. */
81 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
82 LPWSTR text; /* Item text. */
83 ULONG_PTR dwItemData; /* Application defined. */
84 LPWSTR dwTypeData; /* depends on fMask */
85 HBITMAP hbmpItem; /* bitmap */
86 /* ----------- Wine stuff ----------- */
87 RECT rect; /* Item area (relative to menu window) */
88 UINT xTab; /* X position of text after Tab */
89 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
90 * bitmap */
91 } MENUITEM;
93 /* Popup menu structure */
94 typedef struct {
95 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
96 WORD wMagic; /* Magic number */
97 WORD Width; /* Width of the whole menu */
98 WORD Height; /* Height of the whole menu */
99 UINT nItems; /* Number of items in the menu */
100 HWND hWnd; /* Window containing the menu */
101 MENUITEM *items; /* Array of menu items */
102 UINT FocusedItem; /* Currently focused item */
103 HWND hwndOwner; /* window receiving the messages for ownerdraw */
104 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
105 BOOL bScrolling; /* Scroll arrows are active */
106 UINT nScrollPos; /* Current scroll position */
107 UINT nTotalHeight; /* Total height of menu items inside menu */
108 /* ------------ MENUINFO members ------ */
109 DWORD dwStyle; /* Extended menu style */
110 UINT cyMax; /* max height of the whole menu, 0 is screen height */
111 HBRUSH hbrBack; /* brush for menu background */
112 DWORD dwContextHelpID;
113 DWORD dwMenuData; /* application defined value */
114 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
115 SIZE maxBmpSize; /* Maximum size of the bitmap items */
116 } POPUPMENU, *LPPOPUPMENU;
118 /* internal flags for menu tracking */
120 #define TF_ENDMENU 0x10000
121 #define TF_SUSPENDPOPUP 0x20000
122 #define TF_SKIPREMOVE 0x40000
124 typedef struct
126 UINT trackFlags;
127 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
128 HMENU hTopMenu; /* initial menu */
129 HWND hOwnerWnd; /* where notifications are sent */
130 POINT pt;
131 } MTRACKER;
133 #define MENU_MAGIC 0x554d /* 'MU' */
135 #define ITEM_PREV -1
136 #define ITEM_NEXT 1
138 /* Internal MENU_TrackMenu() flags */
139 #define TPM_INTERNAL 0xF0000000
140 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
141 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
143 /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
146 /* top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
150 /* (other menu->FocusedItem values give the position of the focused item) */
151 #define NO_SELECTED_ITEM 0xffff
153 #define MENU_ITEM_TYPE(flags) \
154 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
156 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
157 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
158 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
160 #define IS_SYSTEM_MENU(menu) \
161 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
163 #define MENUITEMINFO_TYPE_MASK \
164 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
165 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
166 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
167 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
168 #define STATE_MASK (~TYPE_MASK)
169 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
171 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
173 static SIZE menucharsize;
174 static UINT ODitemheight; /* default owner drawn item height */
176 /* Use global popup window because there's no way 2 menus can
177 * be tracked at the same time. */
178 static HWND top_popup;
180 /* Flag set by EndMenu() to force an exit from menu tracking */
181 static BOOL fEndMenu = FALSE;
183 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
185 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
187 /*********************************************************************
188 * menu class descriptor
190 const struct builtin_class_descr MENU_builtin_class =
192 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
193 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
194 NULL, /* procA (winproc is Unicode only) */
195 PopupMenuWndProc, /* procW */
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 = USER_HEAP_LIN_ADDR(hMenu);
295 if (!menu || menu->wMagic != MENU_MAGIC)
297 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
298 menu = NULL;
300 return menu;
303 /***********************************************************************
304 * get_win_sys_menu
306 * Get the system menu of a window
308 static HMENU get_win_sys_menu( HWND hwnd )
310 HMENU ret = 0;
311 WND *win = WIN_GetPtr( hwnd );
312 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
314 ret = win->hSysMenu;
315 WIN_ReleasePtr( win );
317 return ret;
320 /***********************************************************************
321 * get_menu_font
323 static HFONT get_menu_font( BOOL bold )
325 static HFONT hMenuFont, hMenuFontBold;
327 HFONT ret = bold ? hMenuFontBold : hMenuFont;
329 if (!ret)
331 NONCLIENTMETRICSW ncm;
332 HFONT prev;
334 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
335 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
337 if (bold)
339 ncm.lfMenuFont.lfWeight += 300;
340 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
342 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
343 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
344 ret, NULL );
345 if (prev)
347 /* another thread beat us to it */
348 DeleteObject( ret );
349 ret = prev;
352 return ret;
355 /***********************************************************************
356 * get_arrow_bitmap
358 static HBITMAP get_arrow_bitmap(void)
360 static HBITMAP arrow_bitmap;
362 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
363 return arrow_bitmap;
366 /***********************************************************************
367 * get_down_arrow_bitmap
369 static HBITMAP get_down_arrow_bitmap(void)
371 static HBITMAP arrow_bitmap;
373 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
374 return arrow_bitmap;
377 /***********************************************************************
378 * get_down_arrow_inactive_bitmap
380 static HBITMAP get_down_arrow_inactive_bitmap(void)
382 static HBITMAP arrow_bitmap;
384 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
385 return arrow_bitmap;
388 /***********************************************************************
389 * get_up_arrow_bitmap
391 static HBITMAP get_up_arrow_bitmap(void)
393 static HBITMAP arrow_bitmap;
395 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
396 return arrow_bitmap;
399 /***********************************************************************
400 * get_up_arrow_inactive_bitmap
402 static HBITMAP get_up_arrow_inactive_bitmap(void)
404 static HBITMAP arrow_bitmap;
406 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
407 return arrow_bitmap;
410 /***********************************************************************
411 * MENU_CopySysPopup
413 * Return the default system menu.
415 static HMENU MENU_CopySysPopup(void)
417 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
418 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
420 if( hMenu ) {
421 MENUINFO minfo;
422 MENUITEMINFOW miteminfo;
423 POPUPMENU* menu = MENU_GetMenu(hMenu);
424 menu->wFlags |= MF_SYSMENU | MF_POPUP;
425 /* decorate the menu with bitmaps */
426 minfo.cbSize = sizeof( MENUINFO);
427 minfo.dwStyle = MNS_CHECKORBMP;
428 minfo.fMask = MIM_STYLE;
429 SetMenuInfo( hMenu, &minfo);
430 miteminfo.cbSize = sizeof( MENUITEMINFOW);
431 miteminfo.fMask = MIIM_BITMAP;
432 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
433 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
434 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
435 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
436 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
437 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
438 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
439 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
440 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
442 else
443 ERR("Unable to load default system menu\n" );
445 TRACE("returning %p.\n", hMenu );
447 return hMenu;
451 /**********************************************************************
452 * MENU_GetSysMenu
454 * Create a copy of the system menu. System menu in Windows is
455 * a special menu bar with the single entry - system menu popup.
456 * This popup is presented to the outside world as a "system menu".
457 * However, the real system menu handle is sometimes seen in the
458 * WM_MENUSELECT parameters (and Word 6 likes it this way).
460 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
462 HMENU hMenu;
464 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
465 if ((hMenu = CreateMenu()))
467 POPUPMENU *menu = MENU_GetMenu(hMenu);
468 menu->wFlags = MF_SYSMENU;
469 menu->hWnd = WIN_GetFullHandle( hWnd );
470 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
472 if (!hPopupMenu)
473 hPopupMenu = MENU_CopySysPopup();
475 if (hPopupMenu)
477 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
478 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
480 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
481 (UINT_PTR)hPopupMenu, NULL );
483 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
484 menu->items[0].fState = 0;
485 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
487 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
488 return hMenu;
490 DestroyMenu( hMenu );
492 ERR("failed to load system menu!\n");
493 return 0;
497 /***********************************************************************
498 * MENU_InitSysMenuPopup
500 * Grey the appropriate items in System menu.
502 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
504 BOOL gray;
506 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
507 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
508 gray = ((style & WS_MAXIMIZE) != 0);
509 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
510 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
511 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
512 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
513 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
514 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
515 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
516 gray = (clsStyle & CS_NOCLOSE) != 0;
518 /* The menu item must keep its state if it's disabled */
519 if(gray)
520 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
524 /******************************************************************************
526 * UINT MENU_GetStartOfNextColumn(
527 * HMENU hMenu )
529 *****************************************************************************/
531 static UINT MENU_GetStartOfNextColumn(
532 HMENU hMenu )
534 POPUPMENU *menu = MENU_GetMenu(hMenu);
535 UINT i;
537 if(!menu)
538 return NO_SELECTED_ITEM;
540 i = menu->FocusedItem + 1;
541 if( i == NO_SELECTED_ITEM )
542 return i;
544 for( ; i < menu->nItems; ++i ) {
545 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
546 return i;
549 return NO_SELECTED_ITEM;
553 /******************************************************************************
555 * UINT MENU_GetStartOfPrevColumn(
556 * HMENU hMenu )
558 *****************************************************************************/
560 static UINT MENU_GetStartOfPrevColumn(
561 HMENU hMenu )
563 POPUPMENU *menu = MENU_GetMenu(hMenu);
564 UINT i;
566 if( !menu )
567 return NO_SELECTED_ITEM;
569 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
570 return NO_SELECTED_ITEM;
572 /* Find the start of the column */
574 for(i = menu->FocusedItem; i != 0 &&
575 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
576 --i); /* empty */
578 if(i == 0)
579 return NO_SELECTED_ITEM;
581 for(--i; i != 0; --i) {
582 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
583 break;
586 TRACE("ret %d.\n", i );
588 return i;
593 /***********************************************************************
594 * MENU_FindItem
596 * Find a menu item. Return a pointer on the item, and modifies *hmenu
597 * in case the item was in a sub-menu.
599 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
601 POPUPMENU *menu;
602 MENUITEM *fallback = NULL;
603 UINT fallback_pos = 0;
604 UINT i;
606 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
607 if (wFlags & MF_BYPOSITION)
609 if (*nPos >= menu->nItems) return NULL;
610 return &menu->items[*nPos];
612 else
614 MENUITEM *item = menu->items;
615 for (i = 0; i < menu->nItems; i++, item++)
617 if (item->fType & MF_POPUP)
619 HMENU hsubmenu = item->hSubMenu;
620 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
621 if (subitem)
623 *hmenu = hsubmenu;
624 return subitem;
626 else if (item->wID == *nPos)
628 /* fallback to this item if nothing else found */
629 fallback_pos = i;
630 fallback = item;
633 else if (item->wID == *nPos)
635 *nPos = i;
636 return item;
641 if (fallback)
642 *nPos = fallback_pos;
644 return fallback;
647 /***********************************************************************
648 * MENU_FindSubMenu
650 * Find a Sub menu. Return the position of the submenu, and modifies
651 * *hmenu in case it is found in another sub-menu.
652 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
654 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
656 POPUPMENU *menu;
657 UINT i;
658 MENUITEM *item;
659 if (((*hmenu)==(HMENU)0xffff) ||
660 (!(menu = MENU_GetMenu(*hmenu))))
661 return NO_SELECTED_ITEM;
662 item = menu->items;
663 for (i = 0; i < menu->nItems; i++, item++) {
664 if(!(item->fType & MF_POPUP)) continue;
665 if (item->hSubMenu == hSubTarget) {
666 return i;
668 else {
669 HMENU hsubmenu = item->hSubMenu;
670 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
671 if (pos != NO_SELECTED_ITEM) {
672 *hmenu = hsubmenu;
673 return pos;
677 return NO_SELECTED_ITEM;
680 /***********************************************************************
681 * MENU_FreeItemData
683 static void MENU_FreeItemData( MENUITEM* item )
685 /* delete text */
686 HeapFree( GetProcessHeap(), 0, item->text );
689 /***********************************************************************
690 * MENU_AdjustMenuItemRect
692 * Adjust menu item rectangle according to scrolling state.
694 static void
695 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
697 if (menu->bScrolling)
699 UINT arrow_bitmap_height;
700 BITMAP bmp;
702 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
703 arrow_bitmap_height = bmp.bmHeight;
704 rect->top += arrow_bitmap_height - menu->nScrollPos;
705 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
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 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
718 POINT pt, UINT *pos )
720 MENUITEM *item;
721 UINT i;
722 RECT rect;
724 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
725 pt.x -= rect.left;
726 pt.y -= rect.top;
727 item = menu->items;
728 for (i = 0; i < menu->nItems; i++, item++)
730 rect = item->rect;
731 MENU_AdjustMenuItemRect(menu, &rect);
732 if (PtInRect(&rect, pt))
734 if (pos) *pos = i;
735 return item;
738 return NULL;
742 /***********************************************************************
743 * MENU_FindItemByKey
745 * Find the menu item selected by a key press.
746 * Return item id, -1 if none, -2 if we should close the menu.
748 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
749 WCHAR key, BOOL forceMenuChar )
751 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
753 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
755 if (hmenu)
757 POPUPMENU *menu = MENU_GetMenu( hmenu );
758 MENUITEM *item = menu->items;
759 LRESULT menuchar;
761 if( !forceMenuChar )
763 UINT i;
765 for (i = 0; i < menu->nItems; i++, item++)
767 if( item->text)
769 WCHAR *p = item->text - 2;
772 p = strchrW (p + 2, '&');
774 while (p != NULL && p [1] == '&');
775 if (p && (toupperW(p[1]) == toupperW(key))) return i;
779 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
780 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
781 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
782 if (HIWORD(menuchar) == 1) return (UINT)(-2);
784 return (UINT)(-1);
788 /***********************************************************************
789 * MENU_GetBitmapItemSize
791 * Get the size of a bitmap item.
793 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
794 HWND hwndOwner)
796 BITMAP bm;
797 HBITMAP bmp = lpitem->hbmpItem;
799 size->cx = size->cy = 0;
801 /* check if there is a magic menu item associated with this item */
802 switch( (INT_PTR) bmp )
804 case (INT_PTR)HBMMENU_CALLBACK:
806 MEASUREITEMSTRUCT measItem;
807 measItem.CtlType = ODT_MENU;
808 measItem.CtlID = 0;
809 measItem.itemID = lpitem->wID;
810 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
811 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
812 measItem.itemData = lpitem->dwItemData;
813 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
814 size->cx = measItem.itemWidth;
815 size->cy = measItem.itemHeight;
816 return;
818 break;
819 case (INT_PTR)HBMMENU_SYSTEM:
820 if (lpitem->dwItemData)
822 bmp = (HBITMAP)lpitem->dwItemData;
823 break;
825 /* fall through */
826 case (INT_PTR)HBMMENU_MBAR_RESTORE:
827 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
828 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
829 case (INT_PTR)HBMMENU_MBAR_CLOSE:
830 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
831 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
832 size->cy = size->cx;
833 return;
834 case (INT_PTR)HBMMENU_POPUP_CLOSE:
835 case (INT_PTR)HBMMENU_POPUP_RESTORE:
836 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
837 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
838 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
839 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
840 return;
842 if (GetObjectW(bmp, sizeof(bm), &bm ))
844 size->cx = bm.bmWidth;
845 size->cy = bm.bmHeight;
849 /***********************************************************************
850 * MENU_DrawBitmapItem
852 * Draw a bitmap item.
854 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
855 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
857 BITMAP bm;
858 DWORD rop;
859 HDC hdcMem;
860 HBITMAP bmp;
861 int w = rect->right - rect->left;
862 int h = rect->bottom - rect->top;
863 int bmp_xoffset = 0;
864 int left, top;
865 HBITMAP hbmToDraw = lpitem->hbmpItem;
866 bmp = hbmToDraw;
868 /* Check if there is a magic menu item associated with this item */
869 if (IS_MAGIC_BITMAP(hbmToDraw))
871 UINT flags = 0;
872 WCHAR bmchr = 0;
873 RECT r;
875 switch((INT_PTR)hbmToDraw)
877 case (INT_PTR)HBMMENU_SYSTEM:
878 if (lpitem->dwItemData)
880 bmp = (HBITMAP)lpitem->dwItemData;
881 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
883 else
885 static HBITMAP hBmpSysMenu;
887 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
888 bmp = hBmpSysMenu;
889 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
890 /* only use right half of the bitmap */
891 bmp_xoffset = bm.bmWidth / 2;
892 bm.bmWidth -= bmp_xoffset;
894 goto got_bitmap;
895 case (INT_PTR)HBMMENU_MBAR_RESTORE:
896 flags = DFCS_CAPTIONRESTORE;
897 break;
898 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
899 flags = DFCS_CAPTIONMIN;
900 break;
901 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
902 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
903 break;
904 case (INT_PTR)HBMMENU_MBAR_CLOSE:
905 flags = DFCS_CAPTIONCLOSE;
906 break;
907 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
908 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
909 break;
910 case (INT_PTR)HBMMENU_CALLBACK:
912 DRAWITEMSTRUCT drawItem;
913 drawItem.CtlType = ODT_MENU;
914 drawItem.CtlID = 0;
915 drawItem.itemID = lpitem->wID;
916 drawItem.itemAction = odaction;
917 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
918 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
919 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
920 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
921 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
922 drawItem.hwndItem = (HWND)hmenu;
923 drawItem.hDC = hdc;
924 drawItem.itemData = lpitem->dwItemData;
925 drawItem.rcItem = *rect;
926 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
927 return;
929 break;
930 case (INT_PTR)HBMMENU_POPUP_CLOSE:
931 bmchr = 0x72;
932 break;
933 case (INT_PTR)HBMMENU_POPUP_RESTORE:
934 bmchr = 0x32;
935 break;
936 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
937 bmchr = 0x31;
938 break;
939 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
940 bmchr = 0x30;
941 break;
942 default:
943 FIXME("Magic %p not implemented\n", hbmToDraw);
944 return;
946 if (bmchr)
948 /* draw the magic bitmaps using marlett font characters */
949 /* FIXME: fontsize and the position (x,y) could probably be better */
950 HFONT hfont, hfontsav;
951 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
952 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
953 { 'M','a','r','l','e','t','t',0 } };
954 logfont.lfHeight = min( h, w) - 5 ;
955 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
956 hfont = CreateFontIndirectW( &logfont);
957 hfontsav = SelectObject(hdc, hfont);
958 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
959 SelectObject(hdc, hfontsav);
960 DeleteObject( hfont);
962 else
964 r = *rect;
965 InflateRect( &r, -1, -1 );
966 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
967 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
969 return;
972 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
974 got_bitmap:
975 hdcMem = CreateCompatibleDC( hdc );
976 SelectObject( hdcMem, bmp );
978 /* handle fontsize > bitmap_height */
979 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
980 left=rect->left;
981 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
982 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
983 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
984 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
985 DeleteDC( hdcMem );
989 /***********************************************************************
990 * MENU_CalcItemSize
992 * Calculate the size of the menu item and store it in lpitem->rect.
994 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
995 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
997 WCHAR *p;
998 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
999 UINT arrow_bitmap_width;
1000 BITMAP bm;
1001 INT itemheight;
1003 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1004 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1005 (menuBar ? " (MenuBar)" : ""));
1007 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1008 arrow_bitmap_width = bm.bmWidth;
1010 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1011 if( !menucharsize.cx ) {
1012 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1013 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1014 * but it is unlikely an application will depend on that */
1015 ODitemheight = HIWORD( GetDialogBaseUnits());
1018 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1020 if (lpitem->fType & MF_OWNERDRAW)
1022 MEASUREITEMSTRUCT mis;
1023 mis.CtlType = ODT_MENU;
1024 mis.CtlID = 0;
1025 mis.itemID = lpitem->wID;
1026 mis.itemData = lpitem->dwItemData;
1027 mis.itemHeight = ODitemheight;
1028 mis.itemWidth = 0;
1029 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1030 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1031 * width of a menufont character to the width of an owner-drawn menu.
1033 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1034 if (menuBar) {
1035 /* under at least win95 you seem to be given a standard
1036 height for the menu and the height value is ignored */
1037 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1038 } else
1039 lpitem->rect.bottom += mis.itemHeight;
1041 TRACE("id=%04lx size=%dx%d\n",
1042 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1043 lpitem->rect.bottom-lpitem->rect.top);
1044 return;
1047 if (lpitem->fType & MF_SEPARATOR)
1049 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1050 if( !menuBar)
1051 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1052 return;
1055 itemheight = 0;
1056 lpitem->xTab = 0;
1058 if (!menuBar) {
1059 if (lpitem->hbmpItem) {
1060 SIZE size;
1062 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1063 /* Keep the size of the bitmap in callback mode to be able
1064 * to draw it correctly */
1065 lpitem->bmpsize = size;
1066 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1067 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1068 lpitem->rect.right += size.cx + 2;
1069 itemheight = size.cy + 2;
1071 if( !(lppop->dwStyle & MNS_NOCHECK))
1072 lpitem->rect.right += check_bitmap_width;
1073 lpitem->rect.right += 4 + menucharsize.cx;
1074 lpitem->xTab = lpitem->rect.right;
1075 lpitem->rect.right += arrow_bitmap_width;
1076 } else if (lpitem->hbmpItem) { /* menuBar */
1077 SIZE size;
1079 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1080 lpitem->bmpsize = size;
1081 lpitem->rect.right += size.cx;
1082 if( lpitem->text) lpitem->rect.right += 2;
1083 itemheight = size.cy;
1086 /* it must be a text item - unless it's the system menu */
1087 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1088 HFONT hfontOld = NULL;
1089 RECT rc = lpitem->rect;
1090 LONG txtheight, txtwidth;
1092 if ( lpitem->fState & MFS_DEFAULT ) {
1093 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1095 if (menuBar) {
1096 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1097 DT_SINGLELINE|DT_CALCRECT);
1098 lpitem->rect.right += rc.right - rc.left;
1099 itemheight = max( max( itemheight, txtheight),
1100 GetSystemMetrics( SM_CYMENU) - 1);
1101 lpitem->rect.right += 2 * menucharsize.cx;
1102 } else {
1103 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1104 RECT tmprc = rc;
1105 LONG tmpheight;
1106 int n = (int)( p - lpitem->text);
1107 /* Item contains a tab (only meaningful in popup menus) */
1108 /* get text size before the tab */
1109 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1110 DT_SINGLELINE|DT_CALCRECT);
1111 txtwidth = rc.right - rc.left;
1112 p += 1; /* advance past the Tab */
1113 /* get text size after the tab */
1114 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1115 DT_SINGLELINE|DT_CALCRECT);
1116 lpitem->xTab += txtwidth;
1117 txtheight = max( txtheight, tmpheight);
1118 txtwidth += menucharsize.cx + /* space for the tab */
1119 tmprc.right - tmprc.left; /* space for the short cut */
1120 } else {
1121 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1122 DT_SINGLELINE|DT_CALCRECT);
1123 txtwidth = rc.right - rc.left;
1124 lpitem->xTab += txtwidth;
1126 lpitem->rect.right += 2 + txtwidth;
1127 itemheight = max( itemheight,
1128 max( txtheight + 2, menucharsize.cy + 4));
1130 if (hfontOld) SelectObject (hdc, hfontOld);
1131 } else if( menuBar) {
1132 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1134 lpitem->rect.bottom += itemheight;
1135 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1139 /***********************************************************************
1140 * MENU_GetMaxPopupHeight
1142 static UINT
1143 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1145 if (lppop->cyMax)
1146 return lppop->cyMax;
1147 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1151 /***********************************************************************
1152 * MENU_PopupMenuCalcSize
1154 * Calculate the size of a popup menu.
1156 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1158 MENUITEM *lpitem;
1159 HDC hdc;
1160 UINT start, i;
1161 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1163 lppop->Width = lppop->Height = 0;
1164 if (lppop->nItems == 0) return;
1165 hdc = GetDC( 0 );
1167 SelectObject( hdc, get_menu_font(FALSE));
1169 start = 0;
1170 maxX = 2 + 1;
1172 lppop->maxBmpSize.cx = 0;
1173 lppop->maxBmpSize.cy = 0;
1175 while (start < lppop->nItems)
1177 lpitem = &lppop->items[start];
1178 orgX = maxX;
1179 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1180 orgX += MENU_COL_SPACE;
1181 orgY = MENU_TOP_MARGIN;
1183 maxTab = maxTabWidth = 0;
1184 /* Parse items until column break or end of menu */
1185 for (i = start; i < lppop->nItems; i++, lpitem++)
1187 if ((i != start) &&
1188 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1190 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1191 maxX = max( maxX, lpitem->rect.right );
1192 orgY = lpitem->rect.bottom;
1193 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1195 maxTab = max( maxTab, lpitem->xTab );
1196 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1200 /* Finish the column (set all items to the largest width found) */
1201 maxX = max( maxX, maxTab + maxTabWidth );
1202 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1204 lpitem->rect.right = maxX;
1205 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1206 lpitem->xTab = maxTab;
1209 lppop->Height = max( lppop->Height, orgY );
1212 lppop->Width = maxX;
1214 /* space for 3d border */
1215 lppop->Height += MENU_BOTTOM_MARGIN;
1216 lppop->Width += 2;
1218 /* Adjust popup height if it exceeds maximum */
1219 maxHeight = MENU_GetMaxPopupHeight(lppop);
1220 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1221 if (lppop->Height >= maxHeight)
1223 lppop->Height = maxHeight;
1224 lppop->bScrolling = TRUE;
1226 else
1228 lppop->bScrolling = FALSE;
1231 ReleaseDC( 0, hdc );
1235 /***********************************************************************
1236 * MENU_MenuBarCalcSize
1238 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1239 * height is off by 1 pixel which causes lengthy window relocations when
1240 * active document window is maximized/restored.
1242 * Calculate the size of the menu bar.
1244 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1245 LPPOPUPMENU lppop, HWND hwndOwner )
1247 MENUITEM *lpitem;
1248 UINT start, i, helpPos;
1249 int orgX, orgY, maxY;
1251 if ((lprect == NULL) || (lppop == NULL)) return;
1252 if (lppop->nItems == 0) return;
1253 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1254 lppop->Width = lprect->right - lprect->left;
1255 lppop->Height = 0;
1256 maxY = lprect->top+1;
1257 start = 0;
1258 helpPos = ~0U;
1259 lppop->maxBmpSize.cx = 0;
1260 lppop->maxBmpSize.cy = 0;
1261 while (start < lppop->nItems)
1263 lpitem = &lppop->items[start];
1264 orgX = lprect->left;
1265 orgY = maxY;
1267 /* Parse items until line break or end of menu */
1268 for (i = start; i < lppop->nItems; i++, lpitem++)
1270 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1271 if ((i != start) &&
1272 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1274 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1275 debug_print_menuitem (" item: ", lpitem, "");
1276 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1278 if (lpitem->rect.right > lprect->right)
1280 if (i != start) break;
1281 else lpitem->rect.right = lprect->right;
1283 maxY = max( maxY, lpitem->rect.bottom );
1284 orgX = lpitem->rect.right;
1287 /* Finish the line (set all items to the largest height found) */
1288 while (start < i) lppop->items[start++].rect.bottom = maxY;
1291 lprect->bottom = maxY;
1292 lppop->Height = lprect->bottom - lprect->top;
1294 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1295 /* the last item (if several lines, only move the last line) */
1296 if (helpPos == ~0U) return;
1297 lpitem = &lppop->items[lppop->nItems-1];
1298 orgY = lpitem->rect.top;
1299 orgX = lprect->right;
1300 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1301 if (lpitem->rect.top != orgY) break; /* Other line */
1302 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1303 lpitem->rect.left += orgX - lpitem->rect.right;
1304 lpitem->rect.right = orgX;
1305 orgX = lpitem->rect.left;
1310 /***********************************************************************
1311 * MENU_DrawScrollArrows
1313 * Draw scroll arrows.
1315 static void
1316 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1318 HDC hdcMem = CreateCompatibleDC(hdc);
1319 HBITMAP hOrigBitmap;
1320 UINT arrow_bitmap_width, arrow_bitmap_height;
1321 BITMAP bmp;
1322 RECT rect;
1324 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1325 arrow_bitmap_width = bmp.bmWidth;
1326 arrow_bitmap_height = bmp.bmHeight;
1329 if (lppop->nScrollPos)
1330 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1331 else
1332 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1333 rect.left = 0;
1334 rect.top = 0;
1335 rect.right = lppop->Width;
1336 rect.bottom = arrow_bitmap_height;
1337 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1338 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1339 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1340 rect.top = lppop->Height - arrow_bitmap_height;
1341 rect.bottom = lppop->Height;
1342 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1343 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1344 SelectObject(hdcMem, get_down_arrow_bitmap());
1345 else
1346 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1347 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1348 lppop->Height - arrow_bitmap_height,
1349 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1350 SelectObject(hdcMem, hOrigBitmap);
1351 DeleteDC(hdcMem);
1355 /***********************************************************************
1356 * draw_popup_arrow
1358 * Draws the popup-menu arrow.
1360 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1361 UINT arrow_bitmap_height)
1363 HDC hdcMem = CreateCompatibleDC( hdc );
1364 HBITMAP hOrigBitmap;
1366 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1367 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1368 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1369 arrow_bitmap_width, arrow_bitmap_height,
1370 hdcMem, 0, 0, SRCCOPY );
1371 SelectObject( hdcMem, hOrigBitmap );
1372 DeleteDC( hdcMem );
1374 /***********************************************************************
1375 * MENU_DrawMenuItem
1377 * Draw a single menu item.
1379 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1380 UINT height, BOOL menuBar, UINT odaction )
1382 RECT rect;
1383 BOOL flat_menu = FALSE;
1384 int bkgnd;
1385 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1386 POPUPMENU *menu = MENU_GetMenu(hmenu);
1387 RECT bmprc;
1389 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1391 if (!menuBar) {
1392 BITMAP bmp;
1393 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1394 arrow_bitmap_width = bmp.bmWidth;
1395 arrow_bitmap_height = bmp.bmHeight;
1398 if (lpitem->fType & MF_SYSMENU)
1400 if( !IsIconic(hwnd) )
1401 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1402 return;
1405 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1406 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1408 /* Setup colors */
1410 if (lpitem->fState & MF_HILITE)
1412 if(menuBar && !flat_menu) {
1413 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1414 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1415 } else {
1416 if(lpitem->fState & MF_GRAYED)
1417 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1418 else
1419 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1420 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1423 else
1425 if (lpitem->fState & MF_GRAYED)
1426 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1427 else
1428 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1429 SetBkColor( hdc, GetSysColor( bkgnd ) );
1432 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1433 rect = lpitem->rect;
1434 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1436 if (lpitem->fType & MF_OWNERDRAW)
1439 ** Experimentation under Windows reveals that an owner-drawn
1440 ** menu is given the rectangle which includes the space it requested
1441 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1442 ** and a popup-menu arrow. This is the value of lpitem->rect.
1443 ** Windows will leave all drawing to the application except for
1444 ** the popup-menu arrow. Windows always draws that itself, after
1445 ** the menu owner has finished drawing.
1447 DRAWITEMSTRUCT dis;
1449 dis.CtlType = ODT_MENU;
1450 dis.CtlID = 0;
1451 dis.itemID = lpitem->wID;
1452 dis.itemData = lpitem->dwItemData;
1453 dis.itemState = 0;
1454 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1455 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1456 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1457 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1458 dis.hwndItem = (HWND)hmenu;
1459 dis.hDC = hdc;
1460 dis.rcItem = rect;
1461 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1462 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1463 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1464 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1465 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1466 /* Draw the popup-menu arrow */
1467 if (lpitem->fType & MF_POPUP)
1468 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1469 arrow_bitmap_height);
1470 return;
1473 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1475 if (lpitem->fState & MF_HILITE)
1477 if (flat_menu)
1479 InflateRect (&rect, -1, -1);
1480 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1481 InflateRect (&rect, 1, 1);
1482 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1484 else
1486 if(menuBar)
1487 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1488 else
1489 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1492 else
1493 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1495 SetBkMode( hdc, TRANSPARENT );
1497 /* vertical separator */
1498 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1500 HPEN oldPen;
1501 RECT rc = rect;
1503 rc.left -= MENU_COL_SPACE / 2 + 1;
1504 rc.top = 3;
1505 rc.bottom = height - 3;
1506 if (flat_menu)
1508 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1509 MoveToEx( hdc, rc.left, rc.top, NULL );
1510 LineTo( hdc, rc.left, rc.bottom );
1511 SelectObject( hdc, oldPen );
1513 else
1514 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1517 /* horizontal separator */
1518 if (lpitem->fType & MF_SEPARATOR)
1520 HPEN oldPen;
1521 RECT rc = rect;
1523 rc.left++;
1524 rc.right--;
1525 rc.top = ( rc.top + rc.bottom) / 2;
1526 if (flat_menu)
1528 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1529 MoveToEx( hdc, rc.left, rc.top, NULL );
1530 LineTo( hdc, rc.right, rc.top );
1531 SelectObject( hdc, oldPen );
1533 else
1534 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1535 return;
1538 /* helper lines for debugging */
1539 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1540 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1541 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1542 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1545 if (lpitem->hbmpItem) {
1546 /* calculate the bitmap rectangle in coordinates relative
1547 * to the item rectangle */
1548 if( menuBar) {
1549 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1550 bmprc.left = 3;
1551 else
1552 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1554 else if (menu->dwStyle & MNS_NOCHECK)
1555 bmprc.left = 4;
1556 else if (menu->dwStyle & MNS_CHECKORBMP)
1557 bmprc.left = 2;
1558 else
1559 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1560 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1561 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1562 bmprc.top = 0;
1563 else
1564 bmprc.top = (rect.bottom - rect.top -
1565 lpitem->bmpsize.cy) / 2;
1566 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1569 if (!menuBar)
1571 HBITMAP bm;
1572 INT y = rect.top + rect.bottom;
1573 RECT rc = rect;
1574 int checked = FALSE;
1575 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1576 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1577 /* Draw the check mark
1579 * FIXME:
1580 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1582 if( !(menu->dwStyle & MNS_NOCHECK)) {
1583 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1584 lpitem->hUnCheckBit;
1585 if (bm) /* we have a custom bitmap */
1587 HDC hdcMem = CreateCompatibleDC( hdc );
1589 SelectObject( hdcMem, bm );
1590 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1591 check_bitmap_width, check_bitmap_height,
1592 hdcMem, 0, 0, SRCCOPY );
1593 DeleteDC( hdcMem );
1594 checked = TRUE;
1596 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1598 RECT r;
1599 HBITMAP bm = CreateBitmap( check_bitmap_width,
1600 check_bitmap_height, 1, 1, NULL );
1601 HDC hdcMem = CreateCompatibleDC( hdc );
1603 SelectObject( hdcMem, bm );
1604 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1605 DrawFrameControl( hdcMem, &r, DFC_MENU,
1606 (lpitem->fType & MFT_RADIOCHECK) ?
1607 DFCS_MENUBULLET : DFCS_MENUCHECK );
1608 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1609 hdcMem, 0, 0, SRCCOPY );
1610 DeleteDC( hdcMem );
1611 DeleteObject( bm );
1612 checked = TRUE;
1615 if( lpitem->hbmpItem &&
1616 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1617 POINT origorg;
1618 /* some applications make this assumption on the DC's origin */
1619 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1620 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1621 odaction, FALSE);
1622 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1624 /* Draw the popup-menu arrow */
1625 if (lpitem->fType & MF_POPUP)
1626 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1627 arrow_bitmap_height);
1628 rect.left += 4;
1629 if( !(menu->dwStyle & MNS_NOCHECK))
1630 rect.left += check_bitmap_width;
1631 rect.right -= arrow_bitmap_width;
1633 else if( lpitem->hbmpItem)
1634 { /* Draw the bitmap */
1635 POINT origorg;
1637 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1638 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1639 odaction, menuBar);
1640 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1642 /* process text if present */
1643 if (lpitem->text)
1645 register int i;
1646 HFONT hfontOld = 0;
1648 UINT uFormat = (menuBar) ?
1649 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1650 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1652 if( !(menu->dwStyle & MNS_CHECKORBMP))
1653 rect.left += menu->maxBmpSize.cx;
1655 if ( lpitem->fState & MFS_DEFAULT )
1657 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1660 if (menuBar) {
1661 if( lpitem->hbmpItem)
1662 rect.left += lpitem->bmpsize.cx;
1663 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1664 rect.left += menucharsize.cx;
1665 rect.right -= menucharsize.cx;
1668 for (i = 0; lpitem->text[i]; i++)
1669 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1670 break;
1672 if(lpitem->fState & MF_GRAYED)
1674 if (!(lpitem->fState & MF_HILITE) )
1676 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1677 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1678 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1679 --rect.left; --rect.top; --rect.right; --rect.bottom;
1681 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1684 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1686 /* paint the shortcut text */
1687 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1689 if (lpitem->text[i] == '\t')
1691 rect.left = lpitem->xTab;
1692 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1694 else
1696 rect.right = lpitem->xTab;
1697 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1700 if(lpitem->fState & MF_GRAYED)
1702 if (!(lpitem->fState & MF_HILITE) )
1704 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1705 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1706 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1707 --rect.left; --rect.top; --rect.right; --rect.bottom;
1709 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1711 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1714 if (hfontOld)
1715 SelectObject (hdc, hfontOld);
1720 /***********************************************************************
1721 * MENU_DrawPopupMenu
1723 * Paint a popup menu.
1725 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1727 HBRUSH hPrevBrush = 0;
1728 RECT rect;
1730 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1732 GetClientRect( hwnd, &rect );
1734 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1735 && (SelectObject( hdc, get_menu_font(FALSE))))
1737 HPEN hPrevPen;
1739 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1741 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1742 if( hPrevPen )
1744 POPUPMENU *menu;
1745 BOOL flat_menu = FALSE;
1747 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1748 if (flat_menu)
1749 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1750 else
1751 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1753 if( (menu = MENU_GetMenu( hmenu )))
1755 /* draw menu items */
1756 if( menu->nItems)
1758 MENUITEM *item;
1759 UINT u;
1761 item = menu->items;
1762 for( u = menu->nItems; u > 0; u--, item++)
1763 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1764 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1766 /* draw scroll arrows */
1767 if (menu->bScrolling)
1768 MENU_DrawScrollArrows(menu, hdc);
1770 } else
1772 SelectObject( hdc, hPrevBrush );
1777 /***********************************************************************
1778 * MENU_DrawMenuBar
1780 * Paint a menu bar. Returns the height of the menu bar.
1781 * called from [windows/nonclient.c]
1783 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1784 BOOL suppress_draw)
1786 LPPOPUPMENU lppop;
1787 HFONT hfontOld = 0;
1788 HMENU hMenu = GetMenu(hwnd);
1790 lppop = MENU_GetMenu( hMenu );
1791 if (lppop == NULL || lprect == NULL)
1793 return GetSystemMetrics(SM_CYMENU);
1796 if (suppress_draw)
1798 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1800 if (lppop->Height == 0)
1801 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1803 lprect->bottom = lprect->top + lppop->Height;
1805 if (hfontOld) SelectObject( hDC, hfontOld);
1806 return lppop->Height;
1808 else
1809 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1813 /***********************************************************************
1814 * MENU_ShowPopup
1816 * Display a popup menu.
1818 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1819 INT x, INT y, INT xanchor, INT yanchor )
1821 POPUPMENU *menu;
1822 INT width, height;
1823 POINT pt;
1824 HMONITOR monitor;
1825 MONITORINFO info;
1827 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1828 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1830 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1831 if (menu->FocusedItem != NO_SELECTED_ITEM)
1833 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1834 menu->FocusedItem = NO_SELECTED_ITEM;
1837 /* store the owner for DrawItem */
1838 menu->hwndOwner = hwndOwner;
1840 menu->nScrollPos = 0;
1841 MENU_PopupMenuCalcSize( menu );
1843 /* adjust popup menu pos so that it fits within the desktop */
1845 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1846 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1848 /* FIXME: should use item rect */
1849 pt.x = x;
1850 pt.y = y;
1851 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1852 info.cbSize = sizeof(info);
1853 GetMonitorInfoW( monitor, &info );
1855 if( flags & TPM_RIGHTALIGN ) x -= width;
1856 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1858 if( flags & TPM_BOTTOMALIGN ) y -= height;
1859 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1861 if( x + width > info.rcWork.right)
1863 if( xanchor && x >= width - xanchor )
1864 x -= width - xanchor;
1866 if( x + width > info.rcWork.right)
1867 x = info.rcWork.right - width;
1869 if( x < info.rcWork.left ) x = info.rcWork.left;
1871 if( y + height > info.rcWork.bottom)
1873 if( yanchor && y >= height + yanchor )
1874 y -= height + yanchor;
1876 if( y + height > info.rcWork.bottom)
1877 y = info.rcWork.bottom - height;
1879 if( y < info.rcWork.top ) y = info.rcWork.top;
1881 /* NOTE: In Windows, top menu popup is not owned. */
1882 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1883 WS_POPUP, x, y, width, height,
1884 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1885 (LPVOID)hmenu );
1886 if( !menu->hWnd ) return FALSE;
1887 if (!top_popup) top_popup = menu->hWnd;
1889 /* Display the window */
1891 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1892 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1893 UpdateWindow( menu->hWnd );
1894 return TRUE;
1898 /***********************************************************************
1899 * MENU_EnsureMenuItemVisible
1901 static void
1902 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1904 if (lppop->bScrolling)
1906 MENUITEM *item = &lppop->items[wIndex];
1907 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1908 UINT nOldPos = lppop->nScrollPos;
1909 RECT rc;
1910 UINT arrow_bitmap_height;
1911 BITMAP bmp;
1913 GetClientRect(lppop->hWnd, &rc);
1915 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1916 arrow_bitmap_height = bmp.bmHeight;
1918 rc.top += arrow_bitmap_height;
1919 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1921 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1922 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1925 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1926 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1927 MENU_DrawScrollArrows(lppop, hdc);
1929 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1931 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1932 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1933 MENU_DrawScrollArrows(lppop, hdc);
1939 /***********************************************************************
1940 * MENU_SelectItem
1942 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1943 BOOL sendMenuSelect, HMENU topmenu )
1945 LPPOPUPMENU lppop;
1946 HDC hdc;
1948 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1950 lppop = MENU_GetMenu( hmenu );
1951 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1953 if (lppop->FocusedItem == wIndex) return;
1954 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1955 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1956 if (!top_popup) top_popup = lppop->hWnd;
1958 SelectObject( hdc, get_menu_font(FALSE));
1960 /* Clear previous highlighted item */
1961 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1963 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1964 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1965 lppop->Height, !(lppop->wFlags & MF_POPUP),
1966 ODA_SELECT );
1969 /* Highlight new item (if any) */
1970 lppop->FocusedItem = wIndex;
1971 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1973 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1974 lppop->items[wIndex].fState |= MF_HILITE;
1975 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1976 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1977 &lppop->items[wIndex], lppop->Height,
1978 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1980 if (sendMenuSelect)
1982 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1983 SendMessageW( hwndOwner, WM_MENUSELECT,
1984 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
1985 ip->fType | ip->fState |
1986 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1989 else if (sendMenuSelect) {
1990 if(topmenu){
1991 int pos;
1992 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1993 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1994 MENUITEM *ip = &ptm->items[pos];
1995 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
1996 ip->fType | ip->fState |
1997 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2001 ReleaseDC( lppop->hWnd, hdc );
2005 /***********************************************************************
2006 * MENU_MoveSelection
2008 * Moves currently selected item according to the offset parameter.
2009 * If there is no selection then it should select the last item if
2010 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2012 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2014 INT i;
2015 POPUPMENU *menu;
2017 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2019 menu = MENU_GetMenu( hmenu );
2020 if ((!menu) || (!menu->items)) return;
2022 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2024 if( menu->nItems == 1 ) return; else
2025 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2026 ; i += offset)
2027 if (!(menu->items[i].fType & MF_SEPARATOR))
2029 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2030 return;
2034 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2035 i >= 0 && i < menu->nItems ; i += offset)
2036 if (!(menu->items[i].fType & MF_SEPARATOR))
2038 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2039 return;
2044 /**********************************************************************
2045 * MENU_SetItemData
2047 * Set an item's flags, id and text ptr. Called by InsertMenu() and
2048 * ModifyMenu().
2050 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
2051 LPCWSTR str )
2053 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2054 TRACE("flags=%x str=%p\n", flags, str);
2056 if (IS_STRING_ITEM(flags))
2058 LPWSTR prevText = item->text;
2059 if (!str)
2061 flags |= MF_SEPARATOR;
2062 item->text = NULL;
2064 else
2066 LPWSTR text;
2067 /* Item beginning with a backspace is a help item */
2068 if (*str == '\b')
2070 flags |= MF_HELP;
2071 str++;
2073 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2074 return FALSE;
2075 strcpyW( text, str );
2076 item->text = text;
2078 item->hbmpItem = NULL;
2079 HeapFree( GetProcessHeap(), 0, prevText );
2081 else if(( flags & MFT_BITMAP)) {
2082 item->hbmpItem = HBITMAP_32(LOWORD(str));
2083 /* setting bitmap clears text */
2084 HeapFree( GetProcessHeap(), 0, item->text );
2085 item->text = NULL;
2088 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2090 if (flags & MF_OWNERDRAW)
2091 item->dwItemData = (DWORD_PTR)str;
2092 else
2093 item->dwItemData = 0;
2095 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2096 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2098 if (flags & MF_POPUP)
2100 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2101 if (menu) menu->wFlags |= MF_POPUP;
2102 else
2104 item->wID = 0;
2105 item->hSubMenu = 0;
2106 item->fType = 0;
2107 item->fState = 0;
2108 return FALSE;
2112 item->wID = id;
2113 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2115 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2116 flags |= MF_POPUP; /* keep popup */
2118 item->fType = flags & TYPE_MASK;
2119 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2120 for ModifyMenu, but Windows accepts it */
2121 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2123 /* Don't call SetRectEmpty here! */
2125 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2126 return TRUE;
2130 /**********************************************************************
2131 * MENU_InsertItem
2133 * Insert (allocate) a new item into a menu.
2135 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2137 MENUITEM *newItems;
2138 POPUPMENU *menu;
2140 if (!(menu = MENU_GetMenu(hMenu)))
2141 return NULL;
2143 /* Find where to insert new item */
2145 if (flags & MF_BYPOSITION) {
2146 if (pos > menu->nItems)
2147 pos = menu->nItems;
2148 } else {
2149 if (!MENU_FindItem( &hMenu, &pos, flags ))
2150 pos = menu->nItems;
2151 else {
2152 if (!(menu = MENU_GetMenu( hMenu )))
2153 return NULL;
2157 /* Make sure that MDI system buttons stay on the right side.
2158 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2159 * regardless of their id.
2161 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2162 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2163 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2164 pos--;
2166 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2168 /* Create new items array */
2170 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2171 if (!newItems)
2173 WARN("allocation failed\n" );
2174 return NULL;
2176 if (menu->nItems > 0)
2178 /* Copy the old array into the new one */
2179 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2180 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2181 (menu->nItems-pos)*sizeof(MENUITEM) );
2182 HeapFree( GetProcessHeap(), 0, menu->items );
2184 menu->items = newItems;
2185 menu->nItems++;
2186 memset( &newItems[pos], 0, sizeof(*newItems) );
2187 menu->Height = 0; /* force size recalculate */
2188 return &newItems[pos];
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, BOOL unicode )
2202 WORD flags, id = 0;
2203 LPCSTR 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 = res;
2219 if (!unicode) res += strlen(str) + 1;
2220 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2221 if (flags & MF_POPUP)
2223 HMENU hSubMenu = CreatePopupMenu();
2224 if (!hSubMenu) return NULL;
2225 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2226 return NULL;
2227 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2228 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2230 else /* Not a popup */
2232 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2233 else AppendMenuW( hMenu, flags, id,
2234 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2236 } while (!end_flag);
2237 return res;
2241 /**********************************************************************
2242 * MENUEX_ParseResource
2244 * Parse an extended menu resource and add items to the menu.
2245 * Return a pointer to the end of the resource.
2247 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2249 WORD resinfo;
2250 do {
2251 MENUITEMINFOW mii;
2253 mii.cbSize = sizeof(mii);
2254 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2255 mii.fType = GET_DWORD(res);
2256 res += sizeof(DWORD);
2257 mii.fState = GET_DWORD(res);
2258 res += sizeof(DWORD);
2259 mii.wID = GET_DWORD(res);
2260 res += sizeof(DWORD);
2261 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2262 res += sizeof(WORD);
2263 /* Align the text on a word boundary. */
2264 res += (~((UINT_PTR)res - 1)) & 1;
2265 mii.dwTypeData = (LPWSTR) res;
2266 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2267 /* Align the following fields on a dword boundary. */
2268 res += (~((UINT_PTR)res - 1)) & 3;
2270 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2271 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2273 if (resinfo & 1) { /* Pop-up? */
2274 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2275 res += sizeof(DWORD);
2276 mii.hSubMenu = CreatePopupMenu();
2277 if (!mii.hSubMenu)
2278 return NULL;
2279 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2280 DestroyMenu(mii.hSubMenu);
2281 return NULL;
2283 mii.fMask |= MIIM_SUBMENU;
2284 mii.fType |= MF_POPUP;
2286 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2288 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2289 mii.wID, mii.fType);
2290 mii.fType |= MF_SEPARATOR;
2292 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2293 } while (!(resinfo & MF_END));
2294 return res;
2298 /***********************************************************************
2299 * MENU_GetSubPopup
2301 * Return the handle of the selected sub-popup menu (if any).
2303 static HMENU MENU_GetSubPopup( HMENU hmenu )
2305 POPUPMENU *menu;
2306 MENUITEM *item;
2308 menu = MENU_GetMenu( hmenu );
2310 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2312 item = &menu->items[menu->FocusedItem];
2313 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2314 return item->hSubMenu;
2315 return 0;
2319 /***********************************************************************
2320 * MENU_HideSubPopups
2322 * Hide the sub-popup menus of this menu.
2324 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2325 BOOL sendMenuSelect, UINT wFlags )
2327 POPUPMENU *menu = MENU_GetMenu( hmenu );
2329 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2331 if (menu && top_popup)
2333 HMENU hsubmenu;
2334 POPUPMENU *submenu;
2335 MENUITEM *item;
2337 if (menu->FocusedItem != NO_SELECTED_ITEM)
2339 item = &menu->items[menu->FocusedItem];
2340 if (!(item->fType & MF_POPUP) ||
2341 !(item->fState & MF_MOUSESELECT)) return;
2342 item->fState &= ~MF_MOUSESELECT;
2343 hsubmenu = item->hSubMenu;
2344 } else return;
2346 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2347 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2348 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2349 DestroyWindow( submenu->hWnd );
2350 submenu->hWnd = 0;
2352 if (!(wFlags & TPM_NONOTIFY))
2353 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2354 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2359 /***********************************************************************
2360 * MENU_ShowSubPopup
2362 * Display the sub-menu of the selected item of this menu.
2363 * Return the handle of the submenu, or hmenu if no submenu to display.
2365 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2366 BOOL selectFirst, UINT wFlags )
2368 RECT rect;
2369 POPUPMENU *menu;
2370 MENUITEM *item;
2371 HDC hdc;
2373 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2375 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2377 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2379 item = &menu->items[menu->FocusedItem];
2380 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2381 return hmenu;
2383 /* message must be sent before using item,
2384 because nearly everything may be changed by the application ! */
2386 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2387 if (!(wFlags & TPM_NONOTIFY))
2388 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2389 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2391 item = &menu->items[menu->FocusedItem];
2392 rect = item->rect;
2394 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2395 if (!(item->fState & MF_HILITE))
2397 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2398 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2400 SelectObject( hdc, get_menu_font(FALSE));
2402 item->fState |= MF_HILITE;
2403 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2404 ReleaseDC( menu->hWnd, hdc );
2406 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2407 item->rect = rect;
2409 item->fState |= MF_MOUSESELECT;
2411 if (IS_SYSTEM_MENU(menu))
2413 MENU_InitSysMenuPopup(item->hSubMenu,
2414 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2415 GetClassLongW( menu->hWnd, GCL_STYLE));
2417 NC_GetSysPopupPos( menu->hWnd, &rect );
2418 rect.top = rect.bottom;
2419 rect.right = GetSystemMetrics(SM_CXSIZE);
2420 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2422 else
2424 GetWindowRect( menu->hWnd, &rect );
2425 if (menu->wFlags & MF_POPUP)
2427 RECT rc = item->rect;
2429 MENU_AdjustMenuItemRect(menu, &rc);
2431 /* The first item in the popup menu has to be at the
2432 same y position as the focused menu item */
2433 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2434 rect.top += rc.top - MENU_TOP_MARGIN;
2435 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2436 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2437 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2439 else
2441 rect.left += item->rect.left;
2442 rect.top += item->rect.bottom;
2443 rect.right = item->rect.right - item->rect.left;
2444 rect.bottom = item->rect.bottom - item->rect.top;
2448 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2449 rect.left, rect.top, rect.right, rect.bottom );
2450 if (selectFirst)
2451 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2452 return item->hSubMenu;
2457 /**********************************************************************
2458 * MENU_IsMenuActive
2460 HWND MENU_IsMenuActive(void)
2462 return top_popup;
2465 /***********************************************************************
2466 * MENU_PtMenu
2468 * Walks menu chain trying to find a menu pt maps to.
2470 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2472 POPUPMENU *menu = MENU_GetMenu( hMenu );
2473 UINT item = menu->FocusedItem;
2474 HMENU ret;
2476 /* try subpopup first (if any) */
2477 ret = (item != NO_SELECTED_ITEM &&
2478 (menu->items[item].fType & MF_POPUP) &&
2479 (menu->items[item].fState & MF_MOUSESELECT))
2480 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2482 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2484 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2485 if( menu->wFlags & MF_POPUP )
2487 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2489 else if (ht == HTSYSMENU)
2490 ret = get_win_sys_menu( menu->hWnd );
2491 else if (ht == HTMENU)
2492 ret = GetMenu( menu->hWnd );
2494 return ret;
2497 /***********************************************************************
2498 * MENU_ExecFocusedItem
2500 * Execute a menu item (for instance when user pressed Enter).
2501 * Return the wID of the executed item. Otherwise, -1 indicating
2502 * that no menu item was executed, -2 if a popup is shown;
2503 * Have to receive the flags for the TrackPopupMenu options to avoid
2504 * sending unwanted message.
2507 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2509 MENUITEM *item;
2510 POPUPMENU *menu = MENU_GetMenu( hMenu );
2512 TRACE("%p hmenu=%p\n", pmt, hMenu);
2514 if (!menu || !menu->nItems ||
2515 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2517 item = &menu->items[menu->FocusedItem];
2519 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2521 if (!(item->fType & MF_POPUP))
2523 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2525 /* If TPM_RETURNCMD is set you return the id, but
2526 do not send a message to the owner */
2527 if(!(wFlags & TPM_RETURNCMD))
2529 if( menu->wFlags & MF_SYSMENU )
2530 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2531 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2532 else
2534 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2535 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2537 if (dwStyle & MNS_NOTIFYBYPOS)
2538 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2539 (LPARAM)hMenu);
2540 else
2541 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2544 return item->wID;
2547 else
2549 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2550 return -2;
2553 return -1;
2556 /***********************************************************************
2557 * MENU_SwitchTracking
2559 * Helper function for menu navigation routines.
2561 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2563 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2564 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2566 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2568 if( pmt->hTopMenu != hPtMenu &&
2569 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2571 /* both are top level menus (system and menu-bar) */
2572 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2573 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2574 pmt->hTopMenu = hPtMenu;
2576 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2577 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2581 /***********************************************************************
2582 * MENU_ButtonDown
2584 * Return TRUE if we can go on with menu tracking.
2586 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2588 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2590 if (hPtMenu)
2592 UINT id = 0;
2593 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2594 MENUITEM *item;
2596 if( IS_SYSTEM_MENU(ptmenu) )
2597 item = ptmenu->items;
2598 else
2599 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2601 if( item )
2603 if( ptmenu->FocusedItem != id )
2604 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2606 /* If the popup menu is not already "popped" */
2607 if(!(item->fState & MF_MOUSESELECT ))
2609 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2612 return TRUE;
2614 /* Else the click was on the menu bar, finish the tracking */
2616 return FALSE;
2619 /***********************************************************************
2620 * MENU_ButtonUp
2622 * Return the value of MENU_ExecFocusedItem if
2623 * the selected item was not a popup. Else open the popup.
2624 * A -1 return value indicates that we go on with menu tracking.
2627 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2629 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2631 if (hPtMenu)
2633 UINT id = 0;
2634 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2635 MENUITEM *item;
2637 if( IS_SYSTEM_MENU(ptmenu) )
2638 item = ptmenu->items;
2639 else
2640 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2642 if( item && (ptmenu->FocusedItem == id ))
2644 debug_print_menuitem ("FocusedItem: ", item, "");
2646 if( !(item->fType & MF_POPUP) )
2648 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2649 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2650 return executedMenuId;
2653 /* If we are dealing with the top-level menu */
2654 /* and this is a click on an already "popped" item: */
2655 /* Stop the menu tracking and close the opened submenus */
2656 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2657 return 0;
2659 ptmenu->bTimeToHide = TRUE;
2661 return -1;
2665 /***********************************************************************
2666 * MENU_MouseMove
2668 * Return TRUE if we can go on with menu tracking.
2670 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2672 UINT id = NO_SELECTED_ITEM;
2673 POPUPMENU *ptmenu = NULL;
2675 if( hPtMenu )
2677 ptmenu = MENU_GetMenu( hPtMenu );
2678 if( IS_SYSTEM_MENU(ptmenu) )
2679 id = 0;
2680 else
2681 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2684 if( id == NO_SELECTED_ITEM )
2686 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2687 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2690 else if( ptmenu->FocusedItem != id )
2692 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2693 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2695 return TRUE;
2699 /***********************************************************************
2700 * MENU_DoNextMenu
2702 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2704 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2706 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2707 BOOL atEnd = FALSE;
2709 /* When skipping left, we need to do something special after the
2710 first menu. */
2711 if (vk == VK_LEFT && menu->FocusedItem == 0)
2713 atEnd = TRUE;
2715 /* When skipping right, for the non-system menu, we need to
2716 handle the last non-special menu item (ie skip any window
2717 icons such as MDI maximize, restore or close) */
2718 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2720 UINT i = menu->FocusedItem + 1;
2721 while (i < menu->nItems) {
2722 if ((menu->items[i].wID >= SC_SIZE &&
2723 menu->items[i].wID <= SC_RESTORE)) {
2724 i++;
2725 } else break;
2727 if (i == menu->nItems) {
2728 atEnd = TRUE;
2731 /* When skipping right, we need to cater for the system menu */
2732 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2734 if (menu->FocusedItem == (menu->nItems - 1)) {
2735 atEnd = TRUE;
2739 if( atEnd )
2741 MDINEXTMENU next_menu;
2742 HMENU hNewMenu;
2743 HWND hNewWnd;
2744 UINT id = 0;
2746 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2747 next_menu.hmenuNext = 0;
2748 next_menu.hwndNext = 0;
2749 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2751 TRACE("%p [%p] -> %p [%p]\n",
2752 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2754 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2756 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2757 hNewWnd = pmt->hOwnerWnd;
2758 if( IS_SYSTEM_MENU(menu) )
2760 /* switch to the menu bar */
2762 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2764 if( vk == VK_LEFT )
2766 menu = MENU_GetMenu( hNewMenu );
2767 id = menu->nItems - 1;
2769 /* Skip backwards over any system predefined icons,
2770 eg. MDI close, restore etc icons */
2771 while ((id > 0) &&
2772 (menu->items[id].wID >= SC_SIZE &&
2773 menu->items[id].wID <= SC_RESTORE)) id--;
2776 else if (style & WS_SYSMENU )
2778 /* switch to the system menu */
2779 hNewMenu = get_win_sys_menu( hNewWnd );
2781 else return FALSE;
2783 else /* application returned a new menu to switch to */
2785 hNewMenu = next_menu.hmenuNext;
2786 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2788 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2790 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2792 if (style & WS_SYSMENU &&
2793 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2795 /* get the real system menu */
2796 hNewMenu = get_win_sys_menu(hNewWnd);
2798 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2800 /* FIXME: Not sure what to do here;
2801 * perhaps try to track hNewMenu as a popup? */
2803 TRACE(" -- got confused.\n");
2804 return FALSE;
2807 else return FALSE;
2810 if( hNewMenu != pmt->hTopMenu )
2812 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2813 FALSE, 0 );
2814 if( pmt->hCurrentMenu != pmt->hTopMenu )
2815 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2818 if( hNewWnd != pmt->hOwnerWnd )
2820 pmt->hOwnerWnd = hNewWnd;
2821 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2824 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2825 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2827 return TRUE;
2829 return FALSE;
2832 /***********************************************************************
2833 * MENU_SuspendPopup
2835 * The idea is not to show the popup if the next input message is
2836 * going to hide it anyway.
2838 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2840 MSG msg;
2842 msg.hwnd = pmt->hOwnerWnd;
2844 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2845 pmt->trackFlags |= TF_SKIPREMOVE;
2847 switch( uMsg )
2849 case WM_KEYDOWN:
2850 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2851 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2853 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2854 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2855 if( msg.message == WM_KEYDOWN &&
2856 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2858 pmt->trackFlags |= TF_SUSPENDPOPUP;
2859 return TRUE;
2862 break;
2865 /* failures go through this */
2866 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2867 return FALSE;
2870 /***********************************************************************
2871 * MENU_KeyEscape
2873 * Handle a VK_ESCAPE key event in a menu.
2875 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2877 BOOL bEndMenu = TRUE;
2879 if (pmt->hCurrentMenu != pmt->hTopMenu)
2881 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2883 if (menu->wFlags & MF_POPUP)
2885 HMENU hmenutmp, hmenuprev;
2887 hmenuprev = hmenutmp = pmt->hTopMenu;
2889 /* close topmost popup */
2890 while (hmenutmp != pmt->hCurrentMenu)
2892 hmenuprev = hmenutmp;
2893 hmenutmp = MENU_GetSubPopup( hmenuprev );
2896 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2897 pmt->hCurrentMenu = hmenuprev;
2898 bEndMenu = FALSE;
2902 return bEndMenu;
2905 /***********************************************************************
2906 * MENU_KeyLeft
2908 * Handle a VK_LEFT key event in a menu.
2910 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2912 POPUPMENU *menu;
2913 HMENU hmenutmp, hmenuprev;
2914 UINT prevcol;
2916 hmenuprev = hmenutmp = pmt->hTopMenu;
2917 menu = MENU_GetMenu( hmenutmp );
2919 /* Try to move 1 column left (if possible) */
2920 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2921 NO_SELECTED_ITEM ) {
2923 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2924 prevcol, TRUE, 0 );
2925 return;
2928 /* close topmost popup */
2929 while (hmenutmp != pmt->hCurrentMenu)
2931 hmenuprev = hmenutmp;
2932 hmenutmp = MENU_GetSubPopup( hmenuprev );
2935 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2936 pmt->hCurrentMenu = hmenuprev;
2938 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2940 /* move menu bar selection if no more popups are left */
2942 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2943 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2945 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2947 /* A sublevel menu was displayed - display the next one
2948 * unless there is another displacement coming up */
2950 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2951 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2952 pmt->hTopMenu, TRUE, wFlags);
2958 /***********************************************************************
2959 * MENU_KeyRight
2961 * Handle a VK_RIGHT key event in a menu.
2963 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2965 HMENU hmenutmp;
2966 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2967 UINT nextcol;
2969 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2970 pmt->hCurrentMenu,
2971 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2972 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2974 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2976 /* If already displaying a popup, try to display sub-popup */
2978 hmenutmp = pmt->hCurrentMenu;
2979 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2981 /* if subpopup was displayed then we are done */
2982 if (hmenutmp != pmt->hCurrentMenu) return;
2985 /* Check to see if there's another column */
2986 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2987 NO_SELECTED_ITEM ) {
2988 TRACE("Going to %d.\n", nextcol );
2989 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2990 nextcol, TRUE, 0 );
2991 return;
2994 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2996 if( pmt->hCurrentMenu != pmt->hTopMenu )
2998 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2999 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
3000 } else hmenutmp = 0;
3002 /* try to move to the next item */
3003 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
3004 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
3006 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
3007 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
3008 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
3009 pmt->hTopMenu, TRUE, wFlags);
3013 static void CALLBACK release_capture( BOOL __normal )
3015 set_capture_window( 0, GUI_INMENUMODE, NULL );
3018 /***********************************************************************
3019 * MENU_TrackMenu
3021 * Menu tracking code.
3023 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
3024 HWND hwnd, const RECT *lprect )
3026 MSG msg;
3027 POPUPMENU *menu;
3028 BOOL fRemove;
3029 INT executedMenuId = -1;
3030 MTRACKER mt;
3031 BOOL enterIdleSent = FALSE;
3032 HWND capture_win;
3034 mt.trackFlags = 0;
3035 mt.hCurrentMenu = hmenu;
3036 mt.hTopMenu = hmenu;
3037 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3038 mt.pt.x = x;
3039 mt.pt.y = y;
3041 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3042 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3044 fEndMenu = FALSE;
3045 if (!(menu = MENU_GetMenu( hmenu )))
3047 WARN("Invalid menu handle %p\n", hmenu);
3048 SetLastError(ERROR_INVALID_MENU_HANDLE);
3049 return FALSE;
3052 if (wFlags & TPM_BUTTONDOWN)
3054 /* Get the result in order to start the tracking or not */
3055 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3056 fEndMenu = !fRemove;
3059 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3061 /* owner may not be visible when tracking a popup, so use the menu itself */
3062 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3063 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3065 __TRY while (!fEndMenu)
3067 menu = MENU_GetMenu( mt.hCurrentMenu );
3068 if (!menu) /* sometimes happens if I do a window manager close */
3069 break;
3071 /* we have to keep the message in the queue until it's
3072 * clear that menu loop is not over yet. */
3074 for (;;)
3076 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3078 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3079 /* remove the message from the queue */
3080 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3082 else
3084 if (!enterIdleSent)
3086 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3087 enterIdleSent = TRUE;
3088 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3090 WaitMessage();
3094 /* check if EndMenu() tried to cancel us, by posting this message */
3095 if(msg.message == WM_CANCELMODE)
3097 /* we are now out of the loop */
3098 fEndMenu = TRUE;
3100 /* remove the message from the queue */
3101 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3103 /* break out of internal loop, ala ESCAPE */
3104 break;
3107 TranslateMessage( &msg );
3108 mt.pt = msg.pt;
3110 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3111 enterIdleSent=FALSE;
3113 fRemove = FALSE;
3114 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3117 * Use the mouse coordinates in lParam instead of those in the MSG
3118 * struct to properly handle synthetic messages. They are already
3119 * in screen coordinates.
3121 mt.pt.x = (short)LOWORD(msg.lParam);
3122 mt.pt.y = (short)HIWORD(msg.lParam);
3124 /* Find a menu for this mouse event */
3125 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3127 switch(msg.message)
3129 /* no WM_NC... messages in captured state */
3131 case WM_RBUTTONDBLCLK:
3132 case WM_RBUTTONDOWN:
3133 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3134 /* fall through */
3135 case WM_LBUTTONDBLCLK:
3136 case WM_LBUTTONDOWN:
3137 /* If the message belongs to the menu, removes it from the queue */
3138 /* Else, end menu tracking */
3139 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3140 fEndMenu = !fRemove;
3141 break;
3143 case WM_RBUTTONUP:
3144 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3145 /* fall through */
3146 case WM_LBUTTONUP:
3147 /* Check if a menu was selected by the mouse */
3148 if (hmenu)
3150 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3151 TRACE("executedMenuId %d\n", executedMenuId);
3153 /* End the loop if executedMenuId is an item ID */
3154 /* or if the job was done (executedMenuId = 0). */
3155 fEndMenu = fRemove = (executedMenuId != -1);
3157 /* No menu was selected by the mouse */
3158 /* if the function was called by TrackPopupMenu, continue
3159 with the menu tracking. If not, stop it */
3160 else
3161 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3163 break;
3165 case WM_MOUSEMOVE:
3166 /* the selected menu item must be changed every time */
3167 /* the mouse moves. */
3169 if (hmenu)
3170 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3172 } /* switch(msg.message) - mouse */
3174 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3176 fRemove = TRUE; /* Keyboard messages are always removed */
3177 switch(msg.message)
3179 case WM_KEYDOWN:
3180 case WM_SYSKEYDOWN:
3181 switch(msg.wParam)
3183 case VK_MENU:
3184 fEndMenu = TRUE;
3185 break;
3187 case VK_HOME:
3188 case VK_END:
3189 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3190 NO_SELECTED_ITEM, FALSE, 0 );
3191 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3192 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3193 break;
3195 case VK_UP:
3196 case VK_DOWN: /* If on menu bar, pull-down the menu */
3198 menu = MENU_GetMenu( mt.hCurrentMenu );
3199 if (!(menu->wFlags & MF_POPUP))
3200 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3201 else /* otherwise try to move selection */
3202 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3203 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3204 break;
3206 case VK_LEFT:
3207 MENU_KeyLeft( &mt, wFlags );
3208 break;
3210 case VK_RIGHT:
3211 MENU_KeyRight( &mt, wFlags );
3212 break;
3214 case VK_ESCAPE:
3215 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3216 break;
3218 case VK_F1:
3220 HELPINFO hi;
3221 hi.cbSize = sizeof(HELPINFO);
3222 hi.iContextType = HELPINFO_MENUITEM;
3223 if (menu->FocusedItem == NO_SELECTED_ITEM)
3224 hi.iCtrlId = 0;
3225 else
3226 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3227 hi.hItemHandle = hmenu;
3228 hi.dwContextId = menu->dwContextHelpID;
3229 hi.MousePos = msg.pt;
3230 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3231 break;
3234 default:
3235 break;
3237 break; /* WM_KEYDOWN */
3239 case WM_CHAR:
3240 case WM_SYSCHAR:
3242 UINT pos;
3244 if (msg.wParam == '\r' || msg.wParam == ' ')
3246 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3247 fEndMenu = (executedMenuId != -2);
3249 break;
3252 /* Hack to avoid control chars. */
3253 /* We will find a better way real soon... */
3254 if (msg.wParam < 32) break;
3256 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3257 LOWORD(msg.wParam), FALSE );
3258 if (pos == (UINT)-2) fEndMenu = TRUE;
3259 else if (pos == (UINT)-1) MessageBeep(0);
3260 else
3262 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3263 TRUE, 0 );
3264 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3265 fEndMenu = (executedMenuId != -2);
3268 break;
3269 } /* switch(msg.message) - kbd */
3271 else
3273 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3274 DispatchMessageW( &msg );
3275 continue;
3278 if (!fEndMenu) fRemove = TRUE;
3280 /* finally remove message from the queue */
3282 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3283 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3284 else mt.trackFlags &= ~TF_SKIPREMOVE;
3286 __FINALLY( release_capture )
3288 /* If dropdown is still painted and the close box is clicked on
3289 then the menu will be destroyed as part of the DispatchMessage above.
3290 This will then invalidate the menu handle in mt.hTopMenu. We should
3291 check for this first. */
3292 if( IsMenu( mt.hTopMenu ) )
3294 menu = MENU_GetMenu( mt.hTopMenu );
3296 if( IsWindow( mt.hOwnerWnd ) )
3298 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3300 if (menu && (menu->wFlags & MF_POPUP))
3302 DestroyWindow( menu->hWnd );
3303 menu->hWnd = 0;
3305 if (!(wFlags & TPM_NONOTIFY))
3306 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3307 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3309 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3310 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3313 /* Reset the variable for hiding menu */
3314 if( menu ) menu->bTimeToHide = FALSE;
3317 /* The return value is only used by TrackPopupMenu */
3318 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3319 if (executedMenuId == -1) executedMenuId = 0;
3320 return executedMenuId;
3323 /***********************************************************************
3324 * MENU_InitTracking
3326 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3328 POPUPMENU *menu;
3330 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3332 HideCaret(0);
3334 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3335 if (!(wFlags & TPM_NONOTIFY))
3336 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3338 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3340 if (!(wFlags & TPM_NONOTIFY))
3342 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3343 /* If an app changed/recreated menu bar entries in WM_INITMENU
3344 * menu sizes will be recalculated once the menu created/shown.
3348 /* This makes the menus of applications built with Delphi work.
3349 * It also enables menus to be displayed in more than one window,
3350 * but there are some bugs left that need to be fixed in this case.
3352 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3354 return TRUE;
3356 /***********************************************************************
3357 * MENU_ExitTracking
3359 static BOOL MENU_ExitTracking(HWND hWnd)
3361 TRACE("hwnd=%p\n", hWnd);
3363 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3364 ShowCaret(0);
3365 top_popup = 0;
3366 return TRUE;
3369 /***********************************************************************
3370 * MENU_TrackMouseMenuBar
3372 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3374 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3376 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3377 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3379 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3381 if (IsMenu(hMenu))
3383 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3384 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3385 MENU_ExitTracking(hWnd);
3390 /***********************************************************************
3391 * MENU_TrackKbdMenuBar
3393 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3395 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3397 UINT uItem = NO_SELECTED_ITEM;
3398 HMENU hTrackMenu;
3399 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3401 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3403 /* find window that has a menu */
3405 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3406 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3408 /* check if we have to track a system menu */
3410 hTrackMenu = GetMenu( hwnd );
3411 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3413 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3414 hTrackMenu = get_win_sys_menu( hwnd );
3415 uItem = 0;
3416 wParam |= HTSYSMENU; /* prevent item lookup */
3419 if (!IsMenu( hTrackMenu )) return;
3421 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3423 if( wChar && wChar != ' ' )
3425 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3426 if ( uItem >= (UINT)(-2) )
3428 if( uItem == (UINT)(-1) ) MessageBeep(0);
3429 /* schedule end of menu tracking */
3430 wFlags |= TF_ENDMENU;
3431 goto track_menu;
3435 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3437 if (!(wParam & HTSYSMENU) || wChar == ' ')
3439 if( uItem == NO_SELECTED_ITEM )
3440 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3441 else
3442 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3445 track_menu:
3446 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3447 MENU_ExitTracking( hwnd );
3450 /**********************************************************************
3451 * TrackPopupMenuEx (USER32.@)
3453 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3454 HWND hWnd, LPTPMPARAMS lpTpm )
3456 BOOL ret = FALSE;
3458 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3459 hMenu, wFlags, x, y, hWnd, lpTpm,
3460 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3462 /* Parameter check */
3463 /* FIXME: this check is performed several times, here and in the called
3464 functions. That could be optimized */
3465 if (!MENU_GetMenu( hMenu ))
3467 SetLastError( ERROR_INVALID_MENU_HANDLE );
3468 return FALSE;
3471 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3473 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3474 if (!(wFlags & TPM_NONOTIFY))
3475 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3477 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3478 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3479 lpTpm ? &lpTpm->rcExclude : NULL );
3480 MENU_ExitTracking(hWnd);
3482 return ret;
3485 /**********************************************************************
3486 * TrackPopupMenu (USER32.@)
3488 * Like the win32 API, the function return the command ID only if the
3489 * flag TPM_RETURNCMD is on.
3492 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3493 INT nReserved, HWND hWnd, const RECT *lpRect )
3495 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3498 /***********************************************************************
3499 * PopupMenuWndProc
3501 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3503 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3505 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3507 switch(message)
3509 case WM_CREATE:
3511 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3512 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3513 return 0;
3516 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3517 return MA_NOACTIVATE;
3519 case WM_PAINT:
3521 PAINTSTRUCT ps;
3522 BeginPaint( hwnd, &ps );
3523 MENU_DrawPopupMenu( hwnd, ps.hdc,
3524 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3525 EndPaint( hwnd, &ps );
3526 return 0;
3528 case WM_ERASEBKGND:
3529 return 1;
3531 case WM_DESTROY:
3532 /* zero out global pointer in case resident popup window was destroyed. */
3533 if (hwnd == top_popup) top_popup = 0;
3534 break;
3536 case WM_SHOWWINDOW:
3538 if( wParam )
3540 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3542 else
3543 SetWindowLongPtrW( hwnd, 0, 0 );
3544 break;
3546 case MM_SETMENUHANDLE:
3547 SetWindowLongPtrW( hwnd, 0, wParam );
3548 break;
3550 case MM_GETMENUHANDLE:
3551 return GetWindowLongPtrW( hwnd, 0 );
3553 default:
3554 return DefWindowProcW( hwnd, message, wParam, lParam );
3556 return 0;
3560 /***********************************************************************
3561 * MENU_GetMenuBarHeight
3563 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3565 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3566 INT orgX, INT orgY )
3568 HDC hdc;
3569 RECT rectBar;
3570 LPPOPUPMENU lppop;
3572 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3574 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3576 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3577 SelectObject( hdc, get_menu_font(FALSE));
3578 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3579 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3580 ReleaseDC( hwnd, hdc );
3581 return lppop->Height;
3585 /*******************************************************************
3586 * ChangeMenuA (USER32.@)
3588 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3589 UINT id, UINT flags )
3591 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3592 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3593 id, data );
3594 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3595 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3596 id, data );
3597 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3598 flags & MF_BYPOSITION ? pos : id,
3599 flags & ~MF_REMOVE );
3600 /* Default: MF_INSERT */
3601 return InsertMenuA( hMenu, pos, flags, id, data );
3605 /*******************************************************************
3606 * ChangeMenuW (USER32.@)
3608 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3609 UINT id, UINT flags )
3611 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3612 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3613 id, data );
3614 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3615 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3616 id, data );
3617 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3618 flags & MF_BYPOSITION ? pos : id,
3619 flags & ~MF_REMOVE );
3620 /* Default: MF_INSERT */
3621 return InsertMenuW( hMenu, pos, flags, id, data );
3625 /*******************************************************************
3626 * CheckMenuItem (USER32.@)
3628 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3630 MENUITEM *item;
3631 DWORD ret;
3633 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3634 ret = item->fState & MF_CHECKED;
3635 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3636 else item->fState &= ~MF_CHECKED;
3637 return ret;
3641 /**********************************************************************
3642 * EnableMenuItem (USER32.@)
3644 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3646 UINT oldflags;
3647 MENUITEM *item;
3648 POPUPMENU *menu;
3650 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3652 /* Get the Popupmenu to access the owner menu */
3653 if (!(menu = MENU_GetMenu(hMenu)))
3654 return (UINT)-1;
3656 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3657 return (UINT)-1;
3659 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3660 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3662 /* If the close item in the system menu change update the close button */
3663 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3665 if (menu->hSysMenuOwner != 0)
3667 RECT rc;
3668 POPUPMENU* parentMenu;
3670 /* Get the parent menu to access*/
3671 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3672 return (UINT)-1;
3674 /* Refresh the frame to reflect the change */
3675 GetWindowRect(parentMenu->hWnd, &rc);
3676 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3677 rc.bottom = 0;
3678 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3682 return oldflags;
3686 /*******************************************************************
3687 * GetMenuStringA (USER32.@)
3689 INT WINAPI GetMenuStringA(
3690 HMENU hMenu, /* [in] menuhandle */
3691 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3692 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3693 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3694 UINT wFlags /* [in] MF_ flags */
3696 MENUITEM *item;
3698 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3699 if (str && nMaxSiz) str[0] = '\0';
3700 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3701 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3702 return 0;
3704 if (!item->text) return 0;
3705 if (!str || !nMaxSiz) return strlenW(item->text);
3706 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3707 str[nMaxSiz-1] = 0;
3708 TRACE("returning %s\n", debugstr_a(str));
3709 return strlen(str);
3713 /*******************************************************************
3714 * GetMenuStringW (USER32.@)
3716 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3717 LPWSTR str, INT nMaxSiz, UINT wFlags )
3719 MENUITEM *item;
3721 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3722 if (str && nMaxSiz) str[0] = '\0';
3723 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3724 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3725 return 0;
3727 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3728 if( !(item->text)) {
3729 str[0] = 0;
3730 return 0;
3732 lstrcpynW( str, item->text, nMaxSiz );
3733 TRACE("returning %s\n", debugstr_w(str));
3734 return strlenW(str);
3738 /**********************************************************************
3739 * HiliteMenuItem (USER32.@)
3741 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3742 UINT wHilite )
3744 LPPOPUPMENU menu;
3745 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3746 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3747 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3748 if (menu->FocusedItem == wItemID) return TRUE;
3749 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3750 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3751 return TRUE;
3755 /**********************************************************************
3756 * GetMenuState (USER32.@)
3758 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3760 MENUITEM *item;
3761 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3762 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3763 debug_print_menuitem (" item: ", item, "");
3764 if (item->fType & MF_POPUP)
3766 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3767 if (!menu) return -1;
3768 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3770 else
3772 /* We used to (from way back then) mask the result to 0xff. */
3773 /* I don't know why and it seems wrong as the documented */
3774 /* return flag MF_SEPARATOR is outside that mask. */
3775 return (item->fType | item->fState);
3780 /**********************************************************************
3781 * GetMenuItemCount (USER32.@)
3783 INT WINAPI GetMenuItemCount( HMENU hMenu )
3785 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3786 if (!menu) return -1;
3787 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3788 return menu->nItems;
3792 /**********************************************************************
3793 * GetMenuItemID (USER32.@)
3795 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3797 MENUITEM * lpmi;
3799 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3800 if (lpmi->fType & MF_POPUP) return -1;
3801 return lpmi->wID;
3806 /*******************************************************************
3807 * InsertMenuW (USER32.@)
3809 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3810 UINT_PTR id, LPCWSTR str )
3812 MENUITEM *item;
3814 if (IS_STRING_ITEM(flags) && str)
3815 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3816 hMenu, pos, flags, id, debugstr_w(str) );
3817 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3818 hMenu, pos, flags, id, str );
3820 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3822 if (!(MENU_SetItemData( item, flags, id, str )))
3824 RemoveMenu( hMenu, pos, flags );
3825 return FALSE;
3828 item->hCheckBit = item->hUnCheckBit = 0;
3829 return TRUE;
3833 /*******************************************************************
3834 * InsertMenuA (USER32.@)
3836 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3837 UINT_PTR id, LPCSTR str )
3839 BOOL ret = FALSE;
3841 if (IS_STRING_ITEM(flags) && str)
3843 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3844 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3845 if (newstr)
3847 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3848 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3849 HeapFree( GetProcessHeap(), 0, newstr );
3851 return ret;
3853 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3857 /*******************************************************************
3858 * AppendMenuA (USER32.@)
3860 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3861 UINT_PTR id, LPCSTR data )
3863 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3867 /*******************************************************************
3868 * AppendMenuW (USER32.@)
3870 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3871 UINT_PTR id, LPCWSTR data )
3873 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3877 /**********************************************************************
3878 * RemoveMenu (USER32.@)
3880 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3882 LPPOPUPMENU menu;
3883 MENUITEM *item;
3885 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3886 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3887 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3889 /* Remove item */
3891 MENU_FreeItemData( item );
3893 if (--menu->nItems == 0)
3895 HeapFree( GetProcessHeap(), 0, menu->items );
3896 menu->items = NULL;
3898 else
3900 while(nPos < menu->nItems)
3902 *item = *(item+1);
3903 item++;
3904 nPos++;
3906 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3907 menu->nItems * sizeof(MENUITEM) );
3909 return TRUE;
3913 /**********************************************************************
3914 * DeleteMenu (USER32.@)
3916 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3918 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3919 if (!item) return FALSE;
3920 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3921 /* nPos is now the position of the item */
3922 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3923 return TRUE;
3927 /*******************************************************************
3928 * ModifyMenuW (USER32.@)
3930 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3931 UINT_PTR id, LPCWSTR str )
3933 MENUITEM *item;
3935 if (IS_STRING_ITEM(flags))
3936 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3937 else
3938 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3940 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3941 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3942 return MENU_SetItemData( item, flags, id, str );
3946 /*******************************************************************
3947 * ModifyMenuA (USER32.@)
3949 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3950 UINT_PTR id, LPCSTR str )
3952 BOOL ret = FALSE;
3954 if (IS_STRING_ITEM(flags) && str)
3956 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3957 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3958 if (newstr)
3960 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3961 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3962 HeapFree( GetProcessHeap(), 0, newstr );
3964 return ret;
3966 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3970 /**********************************************************************
3971 * CreatePopupMenu (USER32.@)
3973 HMENU WINAPI CreatePopupMenu(void)
3975 HMENU hmenu;
3976 POPUPMENU *menu;
3978 if (!(hmenu = CreateMenu())) return 0;
3979 menu = MENU_GetMenu( hmenu );
3980 menu->wFlags |= MF_POPUP;
3981 menu->bTimeToHide = FALSE;
3982 return hmenu;
3986 /**********************************************************************
3987 * GetMenuCheckMarkDimensions (USER.417)
3988 * GetMenuCheckMarkDimensions (USER32.@)
3990 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3992 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3996 /**********************************************************************
3997 * SetMenuItemBitmaps (USER32.@)
3999 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4000 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4002 MENUITEM *item;
4004 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4006 if (!hNewCheck && !hNewUnCheck)
4008 item->fState &= ~MF_USECHECKBITMAPS;
4010 else /* Install new bitmaps */
4012 item->hCheckBit = hNewCheck;
4013 item->hUnCheckBit = hNewUnCheck;
4014 item->fState |= MF_USECHECKBITMAPS;
4016 return TRUE;
4020 /**********************************************************************
4021 * CreateMenu (USER32.@)
4023 HMENU WINAPI CreateMenu(void)
4025 HMENU hMenu;
4026 LPPOPUPMENU menu;
4027 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
4028 menu = USER_HEAP_LIN_ADDR(hMenu);
4030 ZeroMemory(menu, sizeof(POPUPMENU));
4031 menu->wMagic = MENU_MAGIC;
4032 menu->FocusedItem = NO_SELECTED_ITEM;
4033 menu->bTimeToHide = FALSE;
4035 TRACE("return %p\n", hMenu );
4037 return hMenu;
4041 /**********************************************************************
4042 * DestroyMenu (USER32.@)
4044 BOOL WINAPI DestroyMenu( HMENU hMenu )
4046 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
4048 TRACE("(%p)\n", hMenu);
4051 if (!lppop) return FALSE;
4053 lppop->wMagic = 0; /* Mark it as destroyed */
4055 /* DestroyMenu should not destroy system menu popup owner */
4056 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4058 DestroyWindow( lppop->hWnd );
4059 lppop->hWnd = 0;
4062 if (lppop->items) /* recursively destroy submenus */
4064 int i;
4065 MENUITEM *item = lppop->items;
4066 for (i = lppop->nItems; i > 0; i--, item++)
4068 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4069 MENU_FreeItemData( item );
4071 HeapFree( GetProcessHeap(), 0, lppop->items );
4073 USER_HEAP_FREE( hMenu );
4074 return TRUE;
4078 /**********************************************************************
4079 * GetSystemMenu (USER32.@)
4081 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4083 WND *wndPtr = WIN_GetPtr( hWnd );
4084 HMENU retvalue = 0;
4086 if (wndPtr == WND_DESKTOP) return 0;
4087 if (wndPtr == WND_OTHER_PROCESS)
4089 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4091 else if (wndPtr)
4093 if (wndPtr->hSysMenu && bRevert)
4095 DestroyMenu(wndPtr->hSysMenu);
4096 wndPtr->hSysMenu = 0;
4099 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4100 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4102 if( wndPtr->hSysMenu )
4104 POPUPMENU *menu;
4105 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4107 /* Store the dummy sysmenu handle to facilitate the refresh */
4108 /* of the close button if the SC_CLOSE item change */
4109 menu = MENU_GetMenu(retvalue);
4110 if ( menu )
4111 menu->hSysMenuOwner = wndPtr->hSysMenu;
4113 WIN_ReleasePtr( wndPtr );
4115 return bRevert ? 0 : retvalue;
4119 /*******************************************************************
4120 * SetSystemMenu (USER32.@)
4122 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4124 WND *wndPtr = WIN_GetPtr( hwnd );
4126 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4128 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4129 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4130 WIN_ReleasePtr( wndPtr );
4131 return TRUE;
4133 return FALSE;
4137 /**********************************************************************
4138 * GetMenu (USER32.@)
4140 HMENU WINAPI GetMenu( HWND hWnd )
4142 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4143 TRACE("for %p returning %p\n", hWnd, retvalue);
4144 return retvalue;
4147 /**********************************************************************
4148 * GetMenuBarInfo (USER32.@)
4150 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4152 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4153 return FALSE;
4156 /**********************************************************************
4157 * MENU_SetMenu
4159 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4160 * SetWindowPos call that would result if SetMenu were called directly.
4162 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4164 TRACE("(%p, %p);\n", hWnd, hMenu);
4166 if (hMenu && !IsMenu(hMenu))
4168 WARN("hMenu %p is not a menu handle\n", hMenu);
4169 return FALSE;
4171 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4172 return FALSE;
4174 hWnd = WIN_GetFullHandle( hWnd );
4175 if (GetCapture() == hWnd)
4176 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4178 if (hMenu != 0)
4180 LPPOPUPMENU lpmenu;
4182 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4184 lpmenu->hWnd = hWnd;
4185 lpmenu->Height = 0; /* Make sure we recalculate the size */
4187 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4188 return TRUE;
4192 /**********************************************************************
4193 * SetMenu (USER32.@)
4195 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4197 if(!MENU_SetMenu(hWnd, hMenu))
4198 return FALSE;
4200 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4201 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4202 return TRUE;
4206 /**********************************************************************
4207 * GetSubMenu (USER32.@)
4209 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4211 MENUITEM * lpmi;
4213 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4214 if (!(lpmi->fType & MF_POPUP)) return 0;
4215 return lpmi->hSubMenu;
4219 /**********************************************************************
4220 * DrawMenuBar (USER32.@)
4222 BOOL WINAPI DrawMenuBar( HWND hWnd )
4224 LPPOPUPMENU lppop;
4225 HMENU hMenu = GetMenu(hWnd);
4227 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4228 return FALSE;
4229 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4231 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4232 lppop->hwndOwner = hWnd;
4233 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4234 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4235 return TRUE;
4238 /***********************************************************************
4239 * DrawMenuBarTemp (USER32.@)
4241 * UNDOCUMENTED !!
4243 * called by W98SE desk.cpl Control Panel Applet
4245 * Not 100% sure about the param names, but close.
4247 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4249 LPPOPUPMENU lppop;
4250 UINT i,retvalue;
4251 HFONT hfontOld = 0;
4252 BOOL flat_menu = FALSE;
4254 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4256 if (!hMenu)
4257 hMenu = GetMenu(hwnd);
4259 if (!hFont)
4260 hFont = get_menu_font(FALSE);
4262 lppop = MENU_GetMenu( hMenu );
4263 if (lppop == NULL || lprect == NULL)
4265 retvalue = GetSystemMetrics(SM_CYMENU);
4266 goto END;
4269 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4271 hfontOld = SelectObject( hDC, hFont);
4273 if (lppop->Height == 0)
4274 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4276 lprect->bottom = lprect->top + lppop->Height;
4278 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4280 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4281 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4282 LineTo( hDC, lprect->right, lprect->bottom );
4284 if (lppop->nItems == 0)
4286 retvalue = GetSystemMetrics(SM_CYMENU);
4287 goto END;
4290 for (i = 0; i < lppop->nItems; i++)
4292 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4293 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4295 retvalue = lppop->Height;
4297 END:
4298 if (hfontOld) SelectObject (hDC, hfontOld);
4299 return retvalue;
4302 /***********************************************************************
4303 * EndMenu (USER.187)
4304 * EndMenu (USER32.@)
4306 BOOL WINAPI EndMenu(void)
4308 /* if we are in the menu code, and it is active */
4309 if (!fEndMenu && top_popup)
4311 /* terminate the menu handling code */
4312 fEndMenu = TRUE;
4314 /* needs to be posted to wakeup the internal menu handler */
4315 /* which will now terminate the menu, in the event that */
4316 /* the main window was minimized, or lost focus, so we */
4317 /* don't end up with an orphaned menu */
4318 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4320 return fEndMenu;
4324 /***********************************************************************
4325 * LookupMenuHandle (USER.217)
4327 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4329 HMENU hmenu32 = HMENU_32(hmenu);
4330 UINT id32 = id;
4331 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4332 else return HMENU_16(hmenu32);
4336 /**********************************************************************
4337 * LoadMenu (USER.150)
4339 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4341 HRSRC16 hRsrc;
4342 HGLOBAL16 handle;
4343 HMENU16 hMenu;
4345 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4346 if (!name) return 0;
4348 instance = GetExePtr( instance );
4349 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4350 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4351 hMenu = LoadMenuIndirect16(LockResource16(handle));
4352 FreeResource16( handle );
4353 return hMenu;
4357 /*****************************************************************
4358 * LoadMenuA (USER32.@)
4360 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4362 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4363 if (!hrsrc) return 0;
4364 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4368 /*****************************************************************
4369 * LoadMenuW (USER32.@)
4371 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4373 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4374 if (!hrsrc) return 0;
4375 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4379 /**********************************************************************
4380 * LoadMenuIndirect (USER.220)
4382 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4384 HMENU hMenu;
4385 WORD version, offset;
4386 LPCSTR p = template;
4388 TRACE("(%p)\n", template );
4389 version = GET_WORD(p);
4390 p += sizeof(WORD);
4391 if (version)
4393 WARN("version must be 0 for Win16\n" );
4394 return 0;
4396 offset = GET_WORD(p);
4397 p += sizeof(WORD) + offset;
4398 if (!(hMenu = CreateMenu())) return 0;
4399 if (!MENU_ParseResource( p, hMenu, FALSE ))
4401 DestroyMenu( hMenu );
4402 return 0;
4404 return HMENU_16(hMenu);
4408 /**********************************************************************
4409 * LoadMenuIndirectW (USER32.@)
4411 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4413 HMENU hMenu;
4414 WORD version, offset;
4415 LPCSTR p = template;
4417 version = GET_WORD(p);
4418 p += sizeof(WORD);
4419 TRACE("%p, ver %d\n", template, version );
4420 switch (version)
4422 case 0: /* standard format is version of 0 */
4423 offset = GET_WORD(p);
4424 p += sizeof(WORD) + offset;
4425 if (!(hMenu = CreateMenu())) return 0;
4426 if (!MENU_ParseResource( p, hMenu, TRUE ))
4428 DestroyMenu( hMenu );
4429 return 0;
4431 return hMenu;
4432 case 1: /* extended format is version of 1 */
4433 offset = GET_WORD(p);
4434 p += sizeof(WORD) + offset;
4435 if (!(hMenu = CreateMenu())) return 0;
4436 if (!MENUEX_ParseResource( p, hMenu))
4438 DestroyMenu( hMenu );
4439 return 0;
4441 return hMenu;
4442 default:
4443 ERR("version %d not supported.\n", version);
4444 return 0;
4449 /**********************************************************************
4450 * LoadMenuIndirectA (USER32.@)
4452 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4454 return LoadMenuIndirectW( template );
4458 /**********************************************************************
4459 * IsMenu (USER32.@)
4461 BOOL WINAPI IsMenu(HMENU hmenu)
4463 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4465 if (!menu)
4467 SetLastError(ERROR_INVALID_MENU_HANDLE);
4468 return FALSE;
4470 return TRUE;
4473 /**********************************************************************
4474 * GetMenuItemInfo_common
4477 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4478 LPMENUITEMINFOW lpmii, BOOL unicode)
4480 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4482 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4484 if (!menu) {
4485 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4486 return FALSE;
4489 if( lpmii->fMask & MIIM_TYPE) {
4490 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4491 WARN("invalid combination of fMask bits used\n");
4492 /* this does not happen on Win9x/ME */
4493 SetLastError( ERROR_INVALID_PARAMETER);
4494 return FALSE;
4496 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4497 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4498 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4499 if( lpmii->fType & MFT_BITMAP) {
4500 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4501 lpmii->cch = 0;
4502 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4503 /* this does not happen on Win9x/ME */
4504 lpmii->dwTypeData = 0;
4505 lpmii->cch = 0;
4509 /* copy the text string */
4510 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4511 if( !menu->text ) {
4512 if(lpmii->dwTypeData && lpmii->cch) {
4513 lpmii->cch = 0;
4514 if( unicode)
4515 *((WCHAR *)lpmii->dwTypeData) = 0;
4516 else
4517 *((CHAR *)lpmii->dwTypeData) = 0;
4519 } else {
4520 int len;
4521 if (unicode)
4523 len = strlenW(menu->text);
4524 if(lpmii->dwTypeData && lpmii->cch)
4525 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4527 else
4529 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4530 0, NULL, NULL ) - 1;
4531 if(lpmii->dwTypeData && lpmii->cch)
4532 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4533 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4534 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4536 /* if we've copied a substring we return its length */
4537 if(lpmii->dwTypeData && lpmii->cch)
4538 if (lpmii->cch <= len + 1)
4539 lpmii->cch--;
4540 else
4541 lpmii->cch = len;
4542 else {
4543 /* return length of string */
4544 /* not on Win9x/ME if fType & MFT_BITMAP */
4545 lpmii->cch = len;
4550 if (lpmii->fMask & MIIM_FTYPE)
4551 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4553 if (lpmii->fMask & MIIM_BITMAP)
4554 lpmii->hbmpItem = menu->hbmpItem;
4556 if (lpmii->fMask & MIIM_STATE)
4557 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4559 if (lpmii->fMask & MIIM_ID)
4560 lpmii->wID = menu->wID;
4562 if (lpmii->fMask & MIIM_SUBMENU)
4563 lpmii->hSubMenu = menu->hSubMenu;
4564 else {
4565 /* hSubMenu is always cleared
4566 * (not on Win9x/ME ) */
4567 lpmii->hSubMenu = 0;
4570 if (lpmii->fMask & MIIM_CHECKMARKS) {
4571 lpmii->hbmpChecked = menu->hCheckBit;
4572 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4574 if (lpmii->fMask & MIIM_DATA)
4575 lpmii->dwItemData = menu->dwItemData;
4577 return TRUE;
4580 /**********************************************************************
4581 * GetMenuItemInfoA (USER32.@)
4583 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4584 LPMENUITEMINFOA lpmii)
4586 BOOL ret;
4587 MENUITEMINFOA mii;
4588 if( lpmii->cbSize != sizeof( mii) &&
4589 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4590 SetLastError( ERROR_INVALID_PARAMETER);
4591 return FALSE;
4593 memcpy( &mii, lpmii, lpmii->cbSize);
4594 mii.cbSize = sizeof( mii);
4595 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4596 (LPMENUITEMINFOW)&mii, FALSE);
4597 mii.cbSize = lpmii->cbSize;
4598 memcpy( lpmii, &mii, mii.cbSize);
4599 return ret;
4602 /**********************************************************************
4603 * GetMenuItemInfoW (USER32.@)
4605 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4606 LPMENUITEMINFOW lpmii)
4608 BOOL ret;
4609 MENUITEMINFOW mii;
4610 if( lpmii->cbSize != sizeof( mii) &&
4611 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4612 SetLastError( ERROR_INVALID_PARAMETER);
4613 return FALSE;
4615 memcpy( &mii, lpmii, lpmii->cbSize);
4616 mii.cbSize = sizeof( mii);
4617 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4618 mii.cbSize = lpmii->cbSize;
4619 memcpy( lpmii, &mii, mii.cbSize);
4620 return ret;
4624 /* set a menu item text from a ASCII or Unicode string */
4625 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4627 if (!text)
4628 menu->text = NULL;
4629 else if (unicode)
4631 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4632 strcpyW( menu->text, text );
4634 else
4636 LPCSTR str = (LPCSTR)text;
4637 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4638 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4639 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4644 /**********************************************************************
4645 * SetMenuItemInfo_common
4648 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4649 const MENUITEMINFOW *lpmii,
4650 BOOL unicode)
4652 if (!menu) return FALSE;
4654 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4656 if (lpmii->fMask & MIIM_TYPE ) {
4657 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4658 WARN("invalid combination of fMask bits used\n");
4659 /* this does not happen on Win9x/ME */
4660 SetLastError( ERROR_INVALID_PARAMETER);
4661 return FALSE;
4664 /* Remove the old type bits and replace them with the new ones */
4665 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4666 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4668 if (IS_STRING_ITEM(menu->fType)) {
4669 HeapFree(GetProcessHeap(), 0, menu->text);
4670 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4671 } else if( (menu->fType) & MFT_BITMAP)
4672 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4675 if (lpmii->fMask & MIIM_FTYPE ) {
4676 if(( lpmii->fType & MFT_BITMAP)) {
4677 SetLastError( ERROR_INVALID_PARAMETER);
4678 return FALSE;
4680 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4681 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4683 if (lpmii->fMask & MIIM_STRING ) {
4684 /* free the string when used */
4685 HeapFree(GetProcessHeap(), 0, menu->text);
4686 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4689 if (lpmii->fMask & MIIM_STATE)
4691 /* Other menu items having MFS_DEFAULT are not converted
4692 to normal items */
4693 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4696 if (lpmii->fMask & MIIM_ID)
4697 menu->wID = lpmii->wID;
4699 if (lpmii->fMask & MIIM_SUBMENU) {
4700 menu->hSubMenu = lpmii->hSubMenu;
4701 if (menu->hSubMenu) {
4702 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4703 if (subMenu) {
4704 subMenu->wFlags |= MF_POPUP;
4705 menu->fType |= MF_POPUP;
4707 else {
4708 SetLastError( ERROR_INVALID_PARAMETER);
4709 return FALSE;
4712 else
4713 menu->fType &= ~MF_POPUP;
4716 if (lpmii->fMask & MIIM_CHECKMARKS)
4718 menu->hCheckBit = lpmii->hbmpChecked;
4719 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4721 if (lpmii->fMask & MIIM_DATA)
4722 menu->dwItemData = lpmii->dwItemData;
4724 if (lpmii->fMask & MIIM_BITMAP)
4725 menu->hbmpItem = lpmii->hbmpItem;
4727 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4728 menu->fType |= MFT_SEPARATOR;
4730 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4731 return TRUE;
4734 /**********************************************************************
4735 * SetMenuItemInfoA (USER32.@)
4737 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4738 const MENUITEMINFOA *lpmii)
4740 MENUITEMINFOA mii;
4742 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4744 if( lpmii->cbSize != sizeof( mii) &&
4745 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4746 SetLastError( ERROR_INVALID_PARAMETER);
4747 return FALSE;
4749 memcpy( &mii, lpmii, lpmii->cbSize);
4750 if( lpmii->cbSize != sizeof( mii)) {
4751 mii.cbSize = sizeof( mii);
4752 mii.hbmpItem = NULL;
4754 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4755 (const MENUITEMINFOW *)&mii, FALSE);
4758 /**********************************************************************
4759 * SetMenuItemInfoW (USER32.@)
4761 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4762 const MENUITEMINFOW *lpmii)
4764 MENUITEMINFOW mii;
4766 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4768 if( lpmii->cbSize != sizeof( mii) &&
4769 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4770 SetLastError( ERROR_INVALID_PARAMETER);
4771 return FALSE;
4773 memcpy( &mii, lpmii, lpmii->cbSize);
4774 if( lpmii->cbSize != sizeof( mii)) {
4775 mii.cbSize = sizeof( mii);
4776 mii.hbmpItem = NULL;
4778 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4779 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4782 /**********************************************************************
4783 * SetMenuDefaultItem (USER32.@)
4786 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4788 UINT i;
4789 POPUPMENU *menu;
4790 MENUITEM *item;
4792 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4794 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4796 /* reset all default-item flags */
4797 item = menu->items;
4798 for (i = 0; i < menu->nItems; i++, item++)
4800 item->fState &= ~MFS_DEFAULT;
4803 /* no default item */
4804 if ( -1 == uItem)
4806 return TRUE;
4809 item = menu->items;
4810 if ( bypos )
4812 if ( uItem >= menu->nItems ) return FALSE;
4813 item[uItem].fState |= MFS_DEFAULT;
4814 return TRUE;
4816 else
4818 for (i = 0; i < menu->nItems; i++, item++)
4820 if (item->wID == uItem)
4822 item->fState |= MFS_DEFAULT;
4823 return TRUE;
4828 return FALSE;
4831 /**********************************************************************
4832 * GetMenuDefaultItem (USER32.@)
4834 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4836 POPUPMENU *menu;
4837 MENUITEM * item;
4838 UINT i = 0;
4840 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4842 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4844 /* find default item */
4845 item = menu->items;
4847 /* empty menu */
4848 if (! item) return -1;
4850 while ( !( item->fState & MFS_DEFAULT ) )
4852 i++; item++;
4853 if (i >= menu->nItems ) return -1;
4856 /* default: don't return disabled items */
4857 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4859 /* search rekursiv when needed */
4860 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4862 UINT ret;
4863 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4864 if ( -1 != ret ) return ret;
4866 /* when item not found in submenu, return the popup item */
4868 return ( bypos ) ? i : item->wID;
4873 /**********************************************************************
4874 * InsertMenuItemA (USER32.@)
4876 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4877 const MENUITEMINFOA *lpmii)
4879 MENUITEM *item;
4880 MENUITEMINFOA mii;
4882 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4884 if( lpmii->cbSize != sizeof( mii) &&
4885 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4886 SetLastError( ERROR_INVALID_PARAMETER);
4887 return FALSE;
4889 memcpy( &mii, lpmii, lpmii->cbSize);
4890 if( lpmii->cbSize != sizeof( mii)) {
4891 mii.cbSize = sizeof( mii);
4892 mii.hbmpItem = NULL;
4895 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4896 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4900 /**********************************************************************
4901 * InsertMenuItemW (USER32.@)
4903 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4904 const MENUITEMINFOW *lpmii)
4906 MENUITEM *item;
4907 MENUITEMINFOW mii;
4909 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4911 if( lpmii->cbSize != sizeof( mii) &&
4912 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4913 SetLastError( ERROR_INVALID_PARAMETER);
4914 return FALSE;
4916 memcpy( &mii, lpmii, lpmii->cbSize);
4917 if( lpmii->cbSize != sizeof( mii)) {
4918 mii.cbSize = sizeof( mii);
4919 mii.hbmpItem = NULL;
4922 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4923 return SetMenuItemInfo_common(item, &mii, TRUE);
4926 /**********************************************************************
4927 * CheckMenuRadioItem (USER32.@)
4930 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4931 UINT first, UINT last, UINT check,
4932 UINT bypos)
4934 BOOL done = FALSE;
4935 UINT i;
4936 MENUITEM *mi_first = NULL, *mi_check;
4937 HMENU m_first, m_check;
4939 for (i = first; i <= last; i++)
4941 UINT pos = i;
4943 if (!mi_first)
4945 m_first = hMenu;
4946 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4947 if (!mi_first) continue;
4948 mi_check = mi_first;
4949 m_check = m_first;
4951 else
4953 m_check = hMenu;
4954 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4955 if (!mi_check) continue;
4958 if (m_first != m_check) continue;
4959 if (mi_check->fType == MFT_SEPARATOR) continue;
4961 if (i == check)
4963 mi_check->fType |= MFT_RADIOCHECK;
4964 mi_check->fState |= MFS_CHECKED;
4965 done = TRUE;
4967 else
4969 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4970 mi_check->fState &= ~MFS_CHECKED;
4974 return done;
4978 /**********************************************************************
4979 * GetMenuItemRect (USER32.@)
4981 * ATTENTION: Here, the returned values in rect are the screen
4982 * coordinates of the item just like if the menu was
4983 * always on the upper left side of the application.
4986 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4987 LPRECT rect)
4989 POPUPMENU *itemMenu;
4990 MENUITEM *item;
4991 HWND referenceHwnd;
4993 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4995 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4996 referenceHwnd = hwnd;
4998 if(!hwnd)
5000 itemMenu = MENU_GetMenu(hMenu);
5001 if (itemMenu == NULL)
5002 return FALSE;
5004 if(itemMenu->hWnd == 0)
5005 return FALSE;
5006 referenceHwnd = itemMenu->hWnd;
5009 if ((rect == NULL) || (item == NULL))
5010 return FALSE;
5012 *rect = item->rect;
5014 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5016 return TRUE;
5019 /**********************************************************************
5020 * SetMenuInfo (USER32.@)
5022 * FIXME
5023 * actually use the items to draw the menu
5024 * (recalculate and/or redraw)
5026 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5028 POPUPMENU *menu;
5029 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5031 if (lpmi->fMask & MIM_BACKGROUND)
5032 menu->hbrBack = lpmi->hbrBack;
5034 if (lpmi->fMask & MIM_HELPID)
5035 menu->dwContextHelpID = lpmi->dwContextHelpID;
5037 if (lpmi->fMask & MIM_MAXHEIGHT)
5038 menu->cyMax = lpmi->cyMax;
5040 if (lpmi->fMask & MIM_MENUDATA)
5041 menu->dwMenuData = lpmi->dwMenuData;
5043 if (lpmi->fMask & MIM_STYLE)
5044 menu->dwStyle = lpmi->dwStyle;
5046 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5047 int i;
5048 MENUITEM *item = menu->items;
5049 for( i = menu->nItems; i; i--, item++)
5050 if( item->fType & MF_POPUP)
5051 menu_SetMenuInfo( item->hSubMenu, lpmi);
5053 return TRUE;
5056 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5058 TRACE("(%p %p)\n", hMenu, lpmi);
5059 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5060 if( lpmi->fMask & MIM_STYLE) {
5061 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5062 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5063 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5065 return TRUE;
5067 SetLastError( ERROR_INVALID_PARAMETER);
5068 return FALSE;
5071 /**********************************************************************
5072 * GetMenuInfo (USER32.@)
5074 * NOTES
5075 * win98/NT5.0
5078 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5079 { POPUPMENU *menu;
5081 TRACE("(%p %p)\n", hMenu, lpmi);
5083 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5086 if (lpmi->fMask & MIM_BACKGROUND)
5087 lpmi->hbrBack = menu->hbrBack;
5089 if (lpmi->fMask & MIM_HELPID)
5090 lpmi->dwContextHelpID = menu->dwContextHelpID;
5092 if (lpmi->fMask & MIM_MAXHEIGHT)
5093 lpmi->cyMax = menu->cyMax;
5095 if (lpmi->fMask & MIM_MENUDATA)
5096 lpmi->dwMenuData = menu->dwMenuData;
5098 if (lpmi->fMask & MIM_STYLE)
5099 lpmi->dwStyle = menu->dwStyle;
5101 return TRUE;
5103 SetLastError( ERROR_INVALID_PARAMETER);
5104 return FALSE;
5108 /**********************************************************************
5109 * SetMenuContextHelpId (USER32.@)
5111 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5113 LPPOPUPMENU menu;
5115 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5117 if ((menu = MENU_GetMenu(hMenu)))
5119 menu->dwContextHelpID = dwContextHelpID;
5120 return TRUE;
5122 return FALSE;
5126 /**********************************************************************
5127 * GetMenuContextHelpId (USER32.@)
5129 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5131 LPPOPUPMENU menu;
5133 TRACE("(%p)\n", hMenu);
5135 if ((menu = MENU_GetMenu(hMenu)))
5137 return menu->dwContextHelpID;
5139 return 0;
5142 /**********************************************************************
5143 * MenuItemFromPoint (USER32.@)
5145 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5147 POPUPMENU *menu = MENU_GetMenu(hMenu);
5148 UINT pos;
5150 /*FIXME: Do we have to handle hWnd here? */
5151 if (!menu) return -1;
5152 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5153 return pos;
5157 /**********************************************************************
5158 * translate_accelerator
5160 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5161 BYTE fVirt, WORD key, WORD cmd )
5163 INT mask = 0;
5164 UINT mesg = 0;
5166 if (wParam != key) return FALSE;
5168 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5169 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5170 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5172 if (message == WM_CHAR || message == WM_SYSCHAR)
5174 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5176 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5177 goto found;
5180 else
5182 if(fVirt & FVIRTKEY)
5184 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5185 wParam, 0xff & HIWORD(lParam));
5187 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5188 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5190 else
5192 if (!(lParam & 0x01000000)) /* no special_key */
5194 if ((fVirt & FALT) && (lParam & 0x20000000))
5195 { /* ^^ ALT pressed */
5196 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5197 goto found;
5202 return FALSE;
5204 found:
5205 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5206 mesg = 1;
5207 else
5209 HMENU hMenu, hSubMenu, hSysMenu;
5210 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5212 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5213 hSysMenu = get_win_sys_menu( hWnd );
5215 /* find menu item and ask application to initialize it */
5216 /* 1. in the system menu */
5217 hSubMenu = hSysMenu;
5218 nPos = cmd;
5219 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5221 if (GetCapture())
5222 mesg = 2;
5223 if (!IsWindowEnabled(hWnd))
5224 mesg = 3;
5225 else
5227 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5228 if(hSubMenu != hSysMenu)
5230 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5231 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5232 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5234 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5237 else /* 2. in the window's menu */
5239 hSubMenu = hMenu;
5240 nPos = cmd;
5241 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5243 if (GetCapture())
5244 mesg = 2;
5245 if (!IsWindowEnabled(hWnd))
5246 mesg = 3;
5247 else
5249 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5250 if(hSubMenu != hMenu)
5252 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5253 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5254 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5256 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5261 if (mesg == 0)
5263 if (uSysStat != (UINT)-1)
5265 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5266 mesg=4;
5267 else
5268 mesg=WM_SYSCOMMAND;
5270 else
5272 if (uStat != (UINT)-1)
5274 if (IsIconic(hWnd))
5275 mesg=5;
5276 else
5278 if (uStat & (MF_DISABLED|MF_GRAYED))
5279 mesg=6;
5280 else
5281 mesg=WM_COMMAND;
5284 else
5285 mesg=WM_COMMAND;
5290 if( mesg==WM_COMMAND )
5292 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5293 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5295 else if( mesg==WM_SYSCOMMAND )
5297 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5298 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5300 else
5302 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5303 * #0: unknown (please report!)
5304 * #1: for WM_KEYUP,WM_SYSKEYUP
5305 * #2: mouse is captured
5306 * #3: window is disabled
5307 * #4: it's a disabled system menu option
5308 * #5: it's a menu option, but window is iconic
5309 * #6: it's a menu option, but disabled
5311 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5312 if(mesg==0)
5313 ERR_(accel)(" unknown reason - please report!\n");
5315 return TRUE;
5318 /**********************************************************************
5319 * TranslateAcceleratorA (USER32.@)
5320 * TranslateAccelerator (USER32.@)
5322 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5324 /* YES, Accel16! */
5325 LPACCEL16 lpAccelTbl;
5326 int i;
5327 WPARAM wParam;
5329 if (!hWnd || !msg) return 0;
5331 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5333 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5334 return 0;
5337 wParam = msg->wParam;
5339 switch (msg->message)
5341 case WM_KEYDOWN:
5342 case WM_SYSKEYDOWN:
5343 break;
5345 case WM_CHAR:
5346 case WM_SYSCHAR:
5348 char ch = LOWORD(wParam);
5349 WCHAR wch;
5350 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5351 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5353 break;
5355 default:
5356 return 0;
5359 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5360 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5361 i = 0;
5364 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5365 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5366 return 1;
5367 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5369 return 0;
5372 /**********************************************************************
5373 * TranslateAcceleratorW (USER32.@)
5375 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5377 /* YES, Accel16! */
5378 LPACCEL16 lpAccelTbl;
5379 int i;
5381 if (!hWnd || !msg) return 0;
5383 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5385 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5386 return 0;
5389 switch (msg->message)
5391 case WM_KEYDOWN:
5392 case WM_SYSKEYDOWN:
5393 case WM_CHAR:
5394 case WM_SYSCHAR:
5395 break;
5397 default:
5398 return 0;
5401 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5402 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5403 i = 0;
5406 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5407 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5408 return 1;
5409 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5411 return 0;