push 297972334720102b8c295c34d3cd5b0b9aafc558
[wine/hacks.git] / dlls / user32 / menu.c
blob8bcd2353d0873177e4833c33832af1a62e7b199a
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 MAKELONG(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, MAKELONG(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 MAKELONG( 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, 0, 0, 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, MAKELONG(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 );
3451 /**********************************************************************
3452 * TrackPopupMenu (USER32.@)
3454 * Like the win32 API, the function return the command ID only if the
3455 * flag TPM_RETURNCMD is on.
3458 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3459 INT nReserved, HWND hWnd, const RECT *lpRect )
3461 BOOL ret = FALSE;
3463 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3464 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3466 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3468 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3469 if (!(wFlags & TPM_NONOTIFY))
3470 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3472 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3473 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3474 MENU_ExitTracking(hWnd);
3476 return ret;
3479 /**********************************************************************
3480 * TrackPopupMenuEx (USER32.@)
3482 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3483 HWND hWnd, LPTPMPARAMS lpTpm )
3485 FIXME("not fully implemented\n" );
3486 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3487 lpTpm ? &lpTpm->rcExclude : NULL );
3490 /***********************************************************************
3491 * PopupMenuWndProc
3493 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3495 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3497 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3499 switch(message)
3501 case WM_CREATE:
3503 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3504 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3505 return 0;
3508 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3509 return MA_NOACTIVATE;
3511 case WM_PAINT:
3513 PAINTSTRUCT ps;
3514 BeginPaint( hwnd, &ps );
3515 MENU_DrawPopupMenu( hwnd, ps.hdc,
3516 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3517 EndPaint( hwnd, &ps );
3518 return 0;
3520 case WM_ERASEBKGND:
3521 return 1;
3523 case WM_DESTROY:
3524 /* zero out global pointer in case resident popup window was destroyed. */
3525 if (hwnd == top_popup) top_popup = 0;
3526 break;
3528 case WM_SHOWWINDOW:
3530 if( wParam )
3532 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3534 else
3535 SetWindowLongPtrW( hwnd, 0, 0 );
3536 break;
3538 case MM_SETMENUHANDLE:
3539 SetWindowLongPtrW( hwnd, 0, wParam );
3540 break;
3542 case MM_GETMENUHANDLE:
3543 return GetWindowLongPtrW( hwnd, 0 );
3545 default:
3546 return DefWindowProcW( hwnd, message, wParam, lParam );
3548 return 0;
3552 /***********************************************************************
3553 * MENU_GetMenuBarHeight
3555 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3557 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3558 INT orgX, INT orgY )
3560 HDC hdc;
3561 RECT rectBar;
3562 LPPOPUPMENU lppop;
3564 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3566 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3568 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3569 SelectObject( hdc, get_menu_font(FALSE));
3570 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3571 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3572 ReleaseDC( hwnd, hdc );
3573 return lppop->Height;
3577 /*******************************************************************
3578 * ChangeMenuA (USER32.@)
3580 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3581 UINT id, UINT flags )
3583 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3584 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3585 id, data );
3586 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3587 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3588 id, data );
3589 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3590 flags & MF_BYPOSITION ? pos : id,
3591 flags & ~MF_REMOVE );
3592 /* Default: MF_INSERT */
3593 return InsertMenuA( hMenu, pos, flags, id, data );
3597 /*******************************************************************
3598 * ChangeMenuW (USER32.@)
3600 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3601 UINT id, UINT flags )
3603 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3604 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3605 id, data );
3606 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3607 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3608 id, data );
3609 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3610 flags & MF_BYPOSITION ? pos : id,
3611 flags & ~MF_REMOVE );
3612 /* Default: MF_INSERT */
3613 return InsertMenuW( hMenu, pos, flags, id, data );
3617 /*******************************************************************
3618 * CheckMenuItem (USER32.@)
3620 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3622 MENUITEM *item;
3623 DWORD ret;
3625 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3626 ret = item->fState & MF_CHECKED;
3627 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3628 else item->fState &= ~MF_CHECKED;
3629 return ret;
3633 /**********************************************************************
3634 * EnableMenuItem (USER32.@)
3636 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3638 UINT oldflags;
3639 MENUITEM *item;
3640 POPUPMENU *menu;
3642 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3644 /* Get the Popupmenu to access the owner menu */
3645 if (!(menu = MENU_GetMenu(hMenu)))
3646 return (UINT)-1;
3648 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3649 return (UINT)-1;
3651 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3652 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3654 /* If the close item in the system menu change update the close button */
3655 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3657 if (menu->hSysMenuOwner != 0)
3659 RECT rc;
3660 POPUPMENU* parentMenu;
3662 /* Get the parent menu to access*/
3663 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3664 return (UINT)-1;
3666 /* Refresh the frame to reflect the change */
3667 GetWindowRect(parentMenu->hWnd, &rc);
3668 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3669 rc.bottom = 0;
3670 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3674 return oldflags;
3678 /*******************************************************************
3679 * GetMenuStringA (USER32.@)
3681 INT WINAPI GetMenuStringA(
3682 HMENU hMenu, /* [in] menuhandle */
3683 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3684 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3685 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3686 UINT wFlags /* [in] MF_ flags */
3688 MENUITEM *item;
3690 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3691 if (str && nMaxSiz) str[0] = '\0';
3692 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3693 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3694 return 0;
3696 if (!item->text) return 0;
3697 if (!str || !nMaxSiz) return strlenW(item->text);
3698 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3699 str[nMaxSiz-1] = 0;
3700 TRACE("returning %s\n", debugstr_a(str));
3701 return strlen(str);
3705 /*******************************************************************
3706 * GetMenuStringW (USER32.@)
3708 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3709 LPWSTR str, INT nMaxSiz, UINT wFlags )
3711 MENUITEM *item;
3713 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3714 if (str && nMaxSiz) str[0] = '\0';
3715 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3716 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3717 return 0;
3719 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3720 if( !(item->text)) {
3721 str[0] = 0;
3722 return 0;
3724 lstrcpynW( str, item->text, nMaxSiz );
3725 TRACE("returning %s\n", debugstr_w(str));
3726 return strlenW(str);
3730 /**********************************************************************
3731 * HiliteMenuItem (USER32.@)
3733 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3734 UINT wHilite )
3736 LPPOPUPMENU menu;
3737 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3738 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3739 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3740 if (menu->FocusedItem == wItemID) return TRUE;
3741 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3742 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3743 return TRUE;
3747 /**********************************************************************
3748 * GetMenuState (USER32.@)
3750 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3752 MENUITEM *item;
3753 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3754 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3755 debug_print_menuitem (" item: ", item, "");
3756 if (item->fType & MF_POPUP)
3758 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3759 if (!menu) return -1;
3760 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3762 else
3764 /* We used to (from way back then) mask the result to 0xff. */
3765 /* I don't know why and it seems wrong as the documented */
3766 /* return flag MF_SEPARATOR is outside that mask. */
3767 return (item->fType | item->fState);
3772 /**********************************************************************
3773 * GetMenuItemCount (USER32.@)
3775 INT WINAPI GetMenuItemCount( HMENU hMenu )
3777 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3778 if (!menu) return -1;
3779 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3780 return menu->nItems;
3784 /**********************************************************************
3785 * GetMenuItemID (USER32.@)
3787 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3789 MENUITEM * lpmi;
3791 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3792 if (lpmi->fType & MF_POPUP) return -1;
3793 return lpmi->wID;
3798 /*******************************************************************
3799 * InsertMenuW (USER32.@)
3801 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3802 UINT_PTR id, LPCWSTR str )
3804 MENUITEM *item;
3806 if (IS_STRING_ITEM(flags) && str)
3807 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3808 hMenu, pos, flags, id, debugstr_w(str) );
3809 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3810 hMenu, pos, flags, id, str );
3812 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3814 if (!(MENU_SetItemData( item, flags, id, str )))
3816 RemoveMenu( hMenu, pos, flags );
3817 return FALSE;
3820 item->hCheckBit = item->hUnCheckBit = 0;
3821 return TRUE;
3825 /*******************************************************************
3826 * InsertMenuA (USER32.@)
3828 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3829 UINT_PTR id, LPCSTR str )
3831 BOOL ret = FALSE;
3833 if (IS_STRING_ITEM(flags) && str)
3835 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3836 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3837 if (newstr)
3839 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3840 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3841 HeapFree( GetProcessHeap(), 0, newstr );
3843 return ret;
3845 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3849 /*******************************************************************
3850 * AppendMenuA (USER32.@)
3852 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3853 UINT_PTR id, LPCSTR data )
3855 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3859 /*******************************************************************
3860 * AppendMenuW (USER32.@)
3862 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3863 UINT_PTR id, LPCWSTR data )
3865 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3869 /**********************************************************************
3870 * RemoveMenu (USER32.@)
3872 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3874 LPPOPUPMENU menu;
3875 MENUITEM *item;
3877 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3878 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3879 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3881 /* Remove item */
3883 MENU_FreeItemData( item );
3885 if (--menu->nItems == 0)
3887 HeapFree( GetProcessHeap(), 0, menu->items );
3888 menu->items = NULL;
3890 else
3892 while(nPos < menu->nItems)
3894 *item = *(item+1);
3895 item++;
3896 nPos++;
3898 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3899 menu->nItems * sizeof(MENUITEM) );
3901 return TRUE;
3905 /**********************************************************************
3906 * DeleteMenu (USER32.@)
3908 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3910 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3911 if (!item) return FALSE;
3912 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3913 /* nPos is now the position of the item */
3914 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3915 return TRUE;
3919 /*******************************************************************
3920 * ModifyMenuW (USER32.@)
3922 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3923 UINT_PTR id, LPCWSTR str )
3925 MENUITEM *item;
3927 if (IS_STRING_ITEM(flags))
3928 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3929 else
3930 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3932 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3933 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3934 return MENU_SetItemData( item, flags, id, str );
3938 /*******************************************************************
3939 * ModifyMenuA (USER32.@)
3941 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3942 UINT_PTR id, LPCSTR str )
3944 BOOL ret = FALSE;
3946 if (IS_STRING_ITEM(flags) && str)
3948 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3949 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3950 if (newstr)
3952 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3953 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3954 HeapFree( GetProcessHeap(), 0, newstr );
3956 return ret;
3958 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3962 /**********************************************************************
3963 * CreatePopupMenu (USER32.@)
3965 HMENU WINAPI CreatePopupMenu(void)
3967 HMENU hmenu;
3968 POPUPMENU *menu;
3970 if (!(hmenu = CreateMenu())) return 0;
3971 menu = MENU_GetMenu( hmenu );
3972 menu->wFlags |= MF_POPUP;
3973 menu->bTimeToHide = FALSE;
3974 return hmenu;
3978 /**********************************************************************
3979 * GetMenuCheckMarkDimensions (USER.417)
3980 * GetMenuCheckMarkDimensions (USER32.@)
3982 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3984 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3988 /**********************************************************************
3989 * SetMenuItemBitmaps (USER32.@)
3991 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3992 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3994 MENUITEM *item;
3996 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3998 if (!hNewCheck && !hNewUnCheck)
4000 item->fState &= ~MF_USECHECKBITMAPS;
4002 else /* Install new bitmaps */
4004 item->hCheckBit = hNewCheck;
4005 item->hUnCheckBit = hNewUnCheck;
4006 item->fState |= MF_USECHECKBITMAPS;
4008 return TRUE;
4012 /**********************************************************************
4013 * CreateMenu (USER32.@)
4015 HMENU WINAPI CreateMenu(void)
4017 HMENU hMenu;
4018 LPPOPUPMENU menu;
4019 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
4020 menu = USER_HEAP_LIN_ADDR(hMenu);
4022 ZeroMemory(menu, sizeof(POPUPMENU));
4023 menu->wMagic = MENU_MAGIC;
4024 menu->FocusedItem = NO_SELECTED_ITEM;
4025 menu->bTimeToHide = FALSE;
4027 TRACE("return %p\n", hMenu );
4029 return hMenu;
4033 /**********************************************************************
4034 * DestroyMenu (USER32.@)
4036 BOOL WINAPI DestroyMenu( HMENU hMenu )
4038 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
4040 TRACE("(%p)\n", hMenu);
4043 if (!lppop) return FALSE;
4045 lppop->wMagic = 0; /* Mark it as destroyed */
4047 /* DestroyMenu should not destroy system menu popup owner */
4048 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4050 DestroyWindow( lppop->hWnd );
4051 lppop->hWnd = 0;
4054 if (lppop->items) /* recursively destroy submenus */
4056 int i;
4057 MENUITEM *item = lppop->items;
4058 for (i = lppop->nItems; i > 0; i--, item++)
4060 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4061 MENU_FreeItemData( item );
4063 HeapFree( GetProcessHeap(), 0, lppop->items );
4065 USER_HEAP_FREE( hMenu );
4066 return TRUE;
4070 /**********************************************************************
4071 * GetSystemMenu (USER32.@)
4073 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4075 WND *wndPtr = WIN_GetPtr( hWnd );
4076 HMENU retvalue = 0;
4078 if (wndPtr == WND_DESKTOP) return 0;
4079 if (wndPtr == WND_OTHER_PROCESS)
4081 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4083 else if (wndPtr)
4085 if (wndPtr->hSysMenu && bRevert)
4087 DestroyMenu(wndPtr->hSysMenu);
4088 wndPtr->hSysMenu = 0;
4091 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4092 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4094 if( wndPtr->hSysMenu )
4096 POPUPMENU *menu;
4097 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4099 /* Store the dummy sysmenu handle to facilitate the refresh */
4100 /* of the close button if the SC_CLOSE item change */
4101 menu = MENU_GetMenu(retvalue);
4102 if ( menu )
4103 menu->hSysMenuOwner = wndPtr->hSysMenu;
4105 WIN_ReleasePtr( wndPtr );
4107 return bRevert ? 0 : retvalue;
4111 /*******************************************************************
4112 * SetSystemMenu (USER32.@)
4114 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4116 WND *wndPtr = WIN_GetPtr( hwnd );
4118 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4120 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4121 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4122 WIN_ReleasePtr( wndPtr );
4123 return TRUE;
4125 return FALSE;
4129 /**********************************************************************
4130 * GetMenu (USER32.@)
4132 HMENU WINAPI GetMenu( HWND hWnd )
4134 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4135 TRACE("for %p returning %p\n", hWnd, retvalue);
4136 return retvalue;
4139 /**********************************************************************
4140 * GetMenuBarInfo (USER32.@)
4142 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4144 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4145 return FALSE;
4148 /**********************************************************************
4149 * MENU_SetMenu
4151 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4152 * SetWindowPos call that would result if SetMenu were called directly.
4154 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4156 TRACE("(%p, %p);\n", hWnd, hMenu);
4158 if (hMenu && !IsMenu(hMenu))
4160 WARN("hMenu %p is not a menu handle\n", hMenu);
4161 return FALSE;
4163 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4164 return FALSE;
4166 hWnd = WIN_GetFullHandle( hWnd );
4167 if (GetCapture() == hWnd)
4168 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4170 if (hMenu != 0)
4172 LPPOPUPMENU lpmenu;
4174 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4176 lpmenu->hWnd = hWnd;
4177 lpmenu->Height = 0; /* Make sure we recalculate the size */
4179 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4180 return TRUE;
4184 /**********************************************************************
4185 * SetMenu (USER32.@)
4187 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4189 if(!MENU_SetMenu(hWnd, hMenu))
4190 return FALSE;
4192 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4193 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4194 return TRUE;
4198 /**********************************************************************
4199 * GetSubMenu (USER32.@)
4201 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4203 MENUITEM * lpmi;
4205 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4206 if (!(lpmi->fType & MF_POPUP)) return 0;
4207 return lpmi->hSubMenu;
4211 /**********************************************************************
4212 * DrawMenuBar (USER32.@)
4214 BOOL WINAPI DrawMenuBar( HWND hWnd )
4216 LPPOPUPMENU lppop;
4217 HMENU hMenu = GetMenu(hWnd);
4219 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4220 return FALSE;
4221 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4223 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4224 lppop->hwndOwner = hWnd;
4225 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4226 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4227 return TRUE;
4230 /***********************************************************************
4231 * DrawMenuBarTemp (USER32.@)
4233 * UNDOCUMENTED !!
4235 * called by W98SE desk.cpl Control Panel Applet
4237 * Not 100% sure about the param names, but close.
4239 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4241 LPPOPUPMENU lppop;
4242 UINT i,retvalue;
4243 HFONT hfontOld = 0;
4244 BOOL flat_menu = FALSE;
4246 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4248 if (!hMenu)
4249 hMenu = GetMenu(hwnd);
4251 if (!hFont)
4252 hFont = get_menu_font(FALSE);
4254 lppop = MENU_GetMenu( hMenu );
4255 if (lppop == NULL || lprect == NULL)
4257 retvalue = GetSystemMetrics(SM_CYMENU);
4258 goto END;
4261 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4263 hfontOld = SelectObject( hDC, hFont);
4265 if (lppop->Height == 0)
4266 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4268 lprect->bottom = lprect->top + lppop->Height;
4270 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4272 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4273 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4274 LineTo( hDC, lprect->right, lprect->bottom );
4276 if (lppop->nItems == 0)
4278 retvalue = GetSystemMetrics(SM_CYMENU);
4279 goto END;
4282 for (i = 0; i < lppop->nItems; i++)
4284 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4285 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4287 retvalue = lppop->Height;
4289 END:
4290 if (hfontOld) SelectObject (hDC, hfontOld);
4291 return retvalue;
4294 /***********************************************************************
4295 * EndMenu (USER.187)
4296 * EndMenu (USER32.@)
4298 BOOL WINAPI EndMenu(void)
4300 /* if we are in the menu code, and it is active */
4301 if (!fEndMenu && top_popup)
4303 /* terminate the menu handling code */
4304 fEndMenu = TRUE;
4306 /* needs to be posted to wakeup the internal menu handler */
4307 /* which will now terminate the menu, in the event that */
4308 /* the main window was minimized, or lost focus, so we */
4309 /* don't end up with an orphaned menu */
4310 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4312 return fEndMenu;
4316 /***********************************************************************
4317 * LookupMenuHandle (USER.217)
4319 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4321 HMENU hmenu32 = HMENU_32(hmenu);
4322 UINT id32 = id;
4323 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4324 else return HMENU_16(hmenu32);
4328 /**********************************************************************
4329 * LoadMenu (USER.150)
4331 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4333 HRSRC16 hRsrc;
4334 HGLOBAL16 handle;
4335 HMENU16 hMenu;
4337 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4338 if (!name) return 0;
4340 instance = GetExePtr( instance );
4341 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4342 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4343 hMenu = LoadMenuIndirect16(LockResource16(handle));
4344 FreeResource16( handle );
4345 return hMenu;
4349 /*****************************************************************
4350 * LoadMenuA (USER32.@)
4352 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4354 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4355 if (!hrsrc) return 0;
4356 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4360 /*****************************************************************
4361 * LoadMenuW (USER32.@)
4363 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4365 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4366 if (!hrsrc) return 0;
4367 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4371 /**********************************************************************
4372 * LoadMenuIndirect (USER.220)
4374 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4376 HMENU hMenu;
4377 WORD version, offset;
4378 LPCSTR p = template;
4380 TRACE("(%p)\n", template );
4381 version = GET_WORD(p);
4382 p += sizeof(WORD);
4383 if (version)
4385 WARN("version must be 0 for Win16\n" );
4386 return 0;
4388 offset = GET_WORD(p);
4389 p += sizeof(WORD) + offset;
4390 if (!(hMenu = CreateMenu())) return 0;
4391 if (!MENU_ParseResource( p, hMenu, FALSE ))
4393 DestroyMenu( hMenu );
4394 return 0;
4396 return HMENU_16(hMenu);
4400 /**********************************************************************
4401 * LoadMenuIndirectW (USER32.@)
4403 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4405 HMENU hMenu;
4406 WORD version, offset;
4407 LPCSTR p = template;
4409 version = GET_WORD(p);
4410 p += sizeof(WORD);
4411 TRACE("%p, ver %d\n", template, version );
4412 switch (version)
4414 case 0: /* standard format is version of 0 */
4415 offset = GET_WORD(p);
4416 p += sizeof(WORD) + offset;
4417 if (!(hMenu = CreateMenu())) return 0;
4418 if (!MENU_ParseResource( p, hMenu, TRUE ))
4420 DestroyMenu( hMenu );
4421 return 0;
4423 return hMenu;
4424 case 1: /* extended format is version of 1 */
4425 offset = GET_WORD(p);
4426 p += sizeof(WORD) + offset;
4427 if (!(hMenu = CreateMenu())) return 0;
4428 if (!MENUEX_ParseResource( p, hMenu))
4430 DestroyMenu( hMenu );
4431 return 0;
4433 return hMenu;
4434 default:
4435 ERR("version %d not supported.\n", version);
4436 return 0;
4441 /**********************************************************************
4442 * LoadMenuIndirectA (USER32.@)
4444 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4446 return LoadMenuIndirectW( template );
4450 /**********************************************************************
4451 * IsMenu (USER32.@)
4453 BOOL WINAPI IsMenu(HMENU hmenu)
4455 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4457 if (!menu)
4459 SetLastError(ERROR_INVALID_MENU_HANDLE);
4460 return FALSE;
4462 return TRUE;
4465 /**********************************************************************
4466 * GetMenuItemInfo_common
4469 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4470 LPMENUITEMINFOW lpmii, BOOL unicode)
4472 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4474 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4476 if (!menu) {
4477 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4478 return FALSE;
4481 if( lpmii->fMask & MIIM_TYPE) {
4482 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4483 WARN("invalid combination of fMask bits used\n");
4484 /* this does not happen on Win9x/ME */
4485 SetLastError( ERROR_INVALID_PARAMETER);
4486 return FALSE;
4488 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4489 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4490 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4491 if( lpmii->fType & MFT_BITMAP) {
4492 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4493 lpmii->cch = 0;
4494 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4495 /* this does not happen on Win9x/ME */
4496 lpmii->dwTypeData = 0;
4497 lpmii->cch = 0;
4501 /* copy the text string */
4502 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4503 if( !menu->text ) {
4504 if(lpmii->dwTypeData && lpmii->cch) {
4505 lpmii->cch = 0;
4506 if( unicode)
4507 *((WCHAR *)lpmii->dwTypeData) = 0;
4508 else
4509 *((CHAR *)lpmii->dwTypeData) = 0;
4511 } else {
4512 int len;
4513 if (unicode)
4515 len = strlenW(menu->text);
4516 if(lpmii->dwTypeData && lpmii->cch)
4517 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4519 else
4521 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4522 0, NULL, NULL ) - 1;
4523 if(lpmii->dwTypeData && lpmii->cch)
4524 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4525 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4526 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4528 /* if we've copied a substring we return its length */
4529 if(lpmii->dwTypeData && lpmii->cch)
4530 if (lpmii->cch <= len + 1)
4531 lpmii->cch--;
4532 else
4533 lpmii->cch = len;
4534 else {
4535 /* return length of string */
4536 /* not on Win9x/ME if fType & MFT_BITMAP */
4537 lpmii->cch = len;
4542 if (lpmii->fMask & MIIM_FTYPE)
4543 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4545 if (lpmii->fMask & MIIM_BITMAP)
4546 lpmii->hbmpItem = menu->hbmpItem;
4548 if (lpmii->fMask & MIIM_STATE)
4549 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4551 if (lpmii->fMask & MIIM_ID)
4552 lpmii->wID = menu->wID;
4554 if (lpmii->fMask & MIIM_SUBMENU)
4555 lpmii->hSubMenu = menu->hSubMenu;
4556 else {
4557 /* hSubMenu is always cleared
4558 * (not on Win9x/ME ) */
4559 lpmii->hSubMenu = 0;
4562 if (lpmii->fMask & MIIM_CHECKMARKS) {
4563 lpmii->hbmpChecked = menu->hCheckBit;
4564 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4566 if (lpmii->fMask & MIIM_DATA)
4567 lpmii->dwItemData = menu->dwItemData;
4569 return TRUE;
4572 /**********************************************************************
4573 * GetMenuItemInfoA (USER32.@)
4575 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4576 LPMENUITEMINFOA lpmii)
4578 BOOL ret;
4579 MENUITEMINFOA mii;
4580 if( lpmii->cbSize != sizeof( mii) &&
4581 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4582 SetLastError( ERROR_INVALID_PARAMETER);
4583 return FALSE;
4585 memcpy( &mii, lpmii, lpmii->cbSize);
4586 mii.cbSize = sizeof( mii);
4587 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4588 (LPMENUITEMINFOW)&mii, FALSE);
4589 mii.cbSize = lpmii->cbSize;
4590 memcpy( lpmii, &mii, mii.cbSize);
4591 return ret;
4594 /**********************************************************************
4595 * GetMenuItemInfoW (USER32.@)
4597 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4598 LPMENUITEMINFOW lpmii)
4600 BOOL ret;
4601 MENUITEMINFOW mii;
4602 if( lpmii->cbSize != sizeof( mii) &&
4603 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4604 SetLastError( ERROR_INVALID_PARAMETER);
4605 return FALSE;
4607 memcpy( &mii, lpmii, lpmii->cbSize);
4608 mii.cbSize = sizeof( mii);
4609 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4610 mii.cbSize = lpmii->cbSize;
4611 memcpy( lpmii, &mii, mii.cbSize);
4612 return ret;
4616 /* set a menu item text from a ASCII or Unicode string */
4617 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4619 if (!text)
4620 menu->text = NULL;
4621 else if (unicode)
4623 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4624 strcpyW( menu->text, text );
4626 else
4628 LPCSTR str = (LPCSTR)text;
4629 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4630 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4631 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4636 /**********************************************************************
4637 * SetMenuItemInfo_common
4640 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4641 const MENUITEMINFOW *lpmii,
4642 BOOL unicode)
4644 if (!menu) return FALSE;
4646 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4648 if (lpmii->fMask & MIIM_TYPE ) {
4649 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4650 WARN("invalid combination of fMask bits used\n");
4651 /* this does not happen on Win9x/ME */
4652 SetLastError( ERROR_INVALID_PARAMETER);
4653 return FALSE;
4656 /* Remove the old type bits and replace them with the new ones */
4657 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4658 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4660 if (IS_STRING_ITEM(menu->fType)) {
4661 HeapFree(GetProcessHeap(), 0, menu->text);
4662 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4663 } else if( (menu->fType) & MFT_BITMAP)
4664 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4667 if (lpmii->fMask & MIIM_FTYPE ) {
4668 if(( lpmii->fType & MFT_BITMAP)) {
4669 SetLastError( ERROR_INVALID_PARAMETER);
4670 return FALSE;
4672 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4673 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4675 if (lpmii->fMask & MIIM_STRING ) {
4676 /* free the string when used */
4677 HeapFree(GetProcessHeap(), 0, menu->text);
4678 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4681 if (lpmii->fMask & MIIM_STATE)
4683 /* Other menu items having MFS_DEFAULT are not converted
4684 to normal items */
4685 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4688 if (lpmii->fMask & MIIM_ID)
4689 menu->wID = lpmii->wID;
4691 if (lpmii->fMask & MIIM_SUBMENU) {
4692 menu->hSubMenu = lpmii->hSubMenu;
4693 if (menu->hSubMenu) {
4694 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4695 if (subMenu) {
4696 subMenu->wFlags |= MF_POPUP;
4697 menu->fType |= MF_POPUP;
4699 else {
4700 SetLastError( ERROR_INVALID_PARAMETER);
4701 return FALSE;
4704 else
4705 menu->fType &= ~MF_POPUP;
4708 if (lpmii->fMask & MIIM_CHECKMARKS)
4710 menu->hCheckBit = lpmii->hbmpChecked;
4711 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4713 if (lpmii->fMask & MIIM_DATA)
4714 menu->dwItemData = lpmii->dwItemData;
4716 if (lpmii->fMask & MIIM_BITMAP)
4717 menu->hbmpItem = lpmii->hbmpItem;
4719 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4720 menu->fType |= MFT_SEPARATOR;
4722 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4723 return TRUE;
4726 /**********************************************************************
4727 * SetMenuItemInfoA (USER32.@)
4729 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4730 const MENUITEMINFOA *lpmii)
4732 MENUITEMINFOA mii;
4734 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4736 if( lpmii->cbSize != sizeof( mii) &&
4737 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4738 SetLastError( ERROR_INVALID_PARAMETER);
4739 return FALSE;
4741 memcpy( &mii, lpmii, lpmii->cbSize);
4742 if( lpmii->cbSize != sizeof( mii)) {
4743 mii.cbSize = sizeof( mii);
4744 mii.hbmpItem = NULL;
4746 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4747 (const MENUITEMINFOW *)&mii, FALSE);
4750 /**********************************************************************
4751 * SetMenuItemInfoW (USER32.@)
4753 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4754 const MENUITEMINFOW *lpmii)
4756 MENUITEMINFOW mii;
4758 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4760 if( lpmii->cbSize != sizeof( mii) &&
4761 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4762 SetLastError( ERROR_INVALID_PARAMETER);
4763 return FALSE;
4765 memcpy( &mii, lpmii, lpmii->cbSize);
4766 if( lpmii->cbSize != sizeof( mii)) {
4767 mii.cbSize = sizeof( mii);
4768 mii.hbmpItem = NULL;
4770 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4771 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4774 /**********************************************************************
4775 * SetMenuDefaultItem (USER32.@)
4778 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4780 UINT i;
4781 POPUPMENU *menu;
4782 MENUITEM *item;
4784 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4786 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4788 /* reset all default-item flags */
4789 item = menu->items;
4790 for (i = 0; i < menu->nItems; i++, item++)
4792 item->fState &= ~MFS_DEFAULT;
4795 /* no default item */
4796 if ( -1 == uItem)
4798 return TRUE;
4801 item = menu->items;
4802 if ( bypos )
4804 if ( uItem >= menu->nItems ) return FALSE;
4805 item[uItem].fState |= MFS_DEFAULT;
4806 return TRUE;
4808 else
4810 for (i = 0; i < menu->nItems; i++, item++)
4812 if (item->wID == uItem)
4814 item->fState |= MFS_DEFAULT;
4815 return TRUE;
4820 return FALSE;
4823 /**********************************************************************
4824 * GetMenuDefaultItem (USER32.@)
4826 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4828 POPUPMENU *menu;
4829 MENUITEM * item;
4830 UINT i = 0;
4832 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4834 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4836 /* find default item */
4837 item = menu->items;
4839 /* empty menu */
4840 if (! item) return -1;
4842 while ( !( item->fState & MFS_DEFAULT ) )
4844 i++; item++;
4845 if (i >= menu->nItems ) return -1;
4848 /* default: don't return disabled items */
4849 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4851 /* search rekursiv when needed */
4852 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4854 UINT ret;
4855 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4856 if ( -1 != ret ) return ret;
4858 /* when item not found in submenu, return the popup item */
4860 return ( bypos ) ? i : item->wID;
4865 /**********************************************************************
4866 * InsertMenuItemA (USER32.@)
4868 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4869 const MENUITEMINFOA *lpmii)
4871 MENUITEM *item;
4872 MENUITEMINFOA mii;
4874 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4876 if( lpmii->cbSize != sizeof( mii) &&
4877 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4878 SetLastError( ERROR_INVALID_PARAMETER);
4879 return FALSE;
4881 memcpy( &mii, lpmii, lpmii->cbSize);
4882 if( lpmii->cbSize != sizeof( mii)) {
4883 mii.cbSize = sizeof( mii);
4884 mii.hbmpItem = NULL;
4887 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4888 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4892 /**********************************************************************
4893 * InsertMenuItemW (USER32.@)
4895 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4896 const MENUITEMINFOW *lpmii)
4898 MENUITEM *item;
4899 MENUITEMINFOW mii;
4901 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4903 if( lpmii->cbSize != sizeof( mii) &&
4904 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4905 SetLastError( ERROR_INVALID_PARAMETER);
4906 return FALSE;
4908 memcpy( &mii, lpmii, lpmii->cbSize);
4909 if( lpmii->cbSize != sizeof( mii)) {
4910 mii.cbSize = sizeof( mii);
4911 mii.hbmpItem = NULL;
4914 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4915 return SetMenuItemInfo_common(item, &mii, TRUE);
4918 /**********************************************************************
4919 * CheckMenuRadioItem (USER32.@)
4922 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4923 UINT first, UINT last, UINT check,
4924 UINT bypos)
4926 BOOL done = FALSE;
4927 UINT i;
4928 MENUITEM *mi_first = NULL, *mi_check;
4929 HMENU m_first, m_check;
4931 for (i = first; i <= last; i++)
4933 UINT pos = i;
4935 if (!mi_first)
4937 m_first = hMenu;
4938 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4939 if (!mi_first) continue;
4940 mi_check = mi_first;
4941 m_check = m_first;
4943 else
4945 m_check = hMenu;
4946 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4947 if (!mi_check) continue;
4950 if (m_first != m_check) continue;
4951 if (mi_check->fType == MFT_SEPARATOR) continue;
4953 if (i == check)
4955 mi_check->fType |= MFT_RADIOCHECK;
4956 mi_check->fState |= MFS_CHECKED;
4957 done = TRUE;
4959 else
4961 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4962 mi_check->fState &= ~MFS_CHECKED;
4966 return done;
4970 /**********************************************************************
4971 * GetMenuItemRect (USER32.@)
4973 * ATTENTION: Here, the returned values in rect are the screen
4974 * coordinates of the item just like if the menu was
4975 * always on the upper left side of the application.
4978 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4979 LPRECT rect)
4981 POPUPMENU *itemMenu;
4982 MENUITEM *item;
4983 HWND referenceHwnd;
4985 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4987 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4988 referenceHwnd = hwnd;
4990 if(!hwnd)
4992 itemMenu = MENU_GetMenu(hMenu);
4993 if (itemMenu == NULL)
4994 return FALSE;
4996 if(itemMenu->hWnd == 0)
4997 return FALSE;
4998 referenceHwnd = itemMenu->hWnd;
5001 if ((rect == NULL) || (item == NULL))
5002 return FALSE;
5004 *rect = item->rect;
5006 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5008 return TRUE;
5011 /**********************************************************************
5012 * SetMenuInfo (USER32.@)
5014 * FIXME
5015 * actually use the items to draw the menu
5016 * (recalculate and/or redraw)
5018 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5020 POPUPMENU *menu;
5021 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5023 if (lpmi->fMask & MIM_BACKGROUND)
5024 menu->hbrBack = lpmi->hbrBack;
5026 if (lpmi->fMask & MIM_HELPID)
5027 menu->dwContextHelpID = lpmi->dwContextHelpID;
5029 if (lpmi->fMask & MIM_MAXHEIGHT)
5030 menu->cyMax = lpmi->cyMax;
5032 if (lpmi->fMask & MIM_MENUDATA)
5033 menu->dwMenuData = lpmi->dwMenuData;
5035 if (lpmi->fMask & MIM_STYLE)
5036 menu->dwStyle = lpmi->dwStyle;
5038 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5039 int i;
5040 MENUITEM *item = menu->items;
5041 for( i = menu->nItems; i; i--, item++)
5042 if( item->fType & MF_POPUP)
5043 menu_SetMenuInfo( item->hSubMenu, lpmi);
5045 return TRUE;
5048 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5050 TRACE("(%p %p)\n", hMenu, lpmi);
5051 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5052 if( lpmi->fMask & MIM_STYLE) {
5053 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5054 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5055 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5057 return TRUE;
5059 SetLastError( ERROR_INVALID_PARAMETER);
5060 return FALSE;
5063 /**********************************************************************
5064 * GetMenuInfo (USER32.@)
5066 * NOTES
5067 * win98/NT5.0
5070 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5071 { POPUPMENU *menu;
5073 TRACE("(%p %p)\n", hMenu, lpmi);
5075 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5078 if (lpmi->fMask & MIM_BACKGROUND)
5079 lpmi->hbrBack = menu->hbrBack;
5081 if (lpmi->fMask & MIM_HELPID)
5082 lpmi->dwContextHelpID = menu->dwContextHelpID;
5084 if (lpmi->fMask & MIM_MAXHEIGHT)
5085 lpmi->cyMax = menu->cyMax;
5087 if (lpmi->fMask & MIM_MENUDATA)
5088 lpmi->dwMenuData = menu->dwMenuData;
5090 if (lpmi->fMask & MIM_STYLE)
5091 lpmi->dwStyle = menu->dwStyle;
5093 return TRUE;
5095 SetLastError( ERROR_INVALID_PARAMETER);
5096 return FALSE;
5100 /**********************************************************************
5101 * SetMenuContextHelpId (USER32.@)
5103 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5105 LPPOPUPMENU menu;
5107 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5109 if ((menu = MENU_GetMenu(hMenu)))
5111 menu->dwContextHelpID = dwContextHelpID;
5112 return TRUE;
5114 return FALSE;
5118 /**********************************************************************
5119 * GetMenuContextHelpId (USER32.@)
5121 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5123 LPPOPUPMENU menu;
5125 TRACE("(%p)\n", hMenu);
5127 if ((menu = MENU_GetMenu(hMenu)))
5129 return menu->dwContextHelpID;
5131 return 0;
5134 /**********************************************************************
5135 * MenuItemFromPoint (USER32.@)
5137 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5139 POPUPMENU *menu = MENU_GetMenu(hMenu);
5140 UINT pos;
5142 /*FIXME: Do we have to handle hWnd here? */
5143 if (!menu) return -1;
5144 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5145 return pos;
5149 /**********************************************************************
5150 * translate_accelerator
5152 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5153 BYTE fVirt, WORD key, WORD cmd )
5155 INT mask = 0;
5156 UINT mesg = 0;
5158 if (wParam != key) return FALSE;
5160 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5161 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5162 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5164 if (message == WM_CHAR || message == WM_SYSCHAR)
5166 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5168 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5169 goto found;
5172 else
5174 if(fVirt & FVIRTKEY)
5176 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5177 wParam, 0xff & HIWORD(lParam));
5179 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5180 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5182 else
5184 if (!(lParam & 0x01000000)) /* no special_key */
5186 if ((fVirt & FALT) && (lParam & 0x20000000))
5187 { /* ^^ ALT pressed */
5188 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5189 goto found;
5194 return FALSE;
5196 found:
5197 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5198 mesg = 1;
5199 else
5201 HMENU hMenu, hSubMenu, hSysMenu;
5202 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5204 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5205 hSysMenu = get_win_sys_menu( hWnd );
5207 /* find menu item and ask application to initialize it */
5208 /* 1. in the system menu */
5209 hSubMenu = hSysMenu;
5210 nPos = cmd;
5211 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5213 if (GetCapture())
5214 mesg = 2;
5215 if (!IsWindowEnabled(hWnd))
5216 mesg = 3;
5217 else
5219 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5220 if(hSubMenu != hSysMenu)
5222 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5223 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5224 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5226 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5229 else /* 2. in the window's menu */
5231 hSubMenu = hMenu;
5232 nPos = cmd;
5233 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5235 if (GetCapture())
5236 mesg = 2;
5237 if (!IsWindowEnabled(hWnd))
5238 mesg = 3;
5239 else
5241 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5242 if(hSubMenu != hMenu)
5244 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5245 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5246 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5248 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5253 if (mesg == 0)
5255 if (uSysStat != (UINT)-1)
5257 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5258 mesg=4;
5259 else
5260 mesg=WM_SYSCOMMAND;
5262 else
5264 if (uStat != (UINT)-1)
5266 if (IsIconic(hWnd))
5267 mesg=5;
5268 else
5270 if (uStat & (MF_DISABLED|MF_GRAYED))
5271 mesg=6;
5272 else
5273 mesg=WM_COMMAND;
5276 else
5277 mesg=WM_COMMAND;
5282 if( mesg==WM_COMMAND )
5284 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5285 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5287 else if( mesg==WM_SYSCOMMAND )
5289 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5290 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5292 else
5294 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5295 * #0: unknown (please report!)
5296 * #1: for WM_KEYUP,WM_SYSKEYUP
5297 * #2: mouse is captured
5298 * #3: window is disabled
5299 * #4: it's a disabled system menu option
5300 * #5: it's a menu option, but window is iconic
5301 * #6: it's a menu option, but disabled
5303 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5304 if(mesg==0)
5305 ERR_(accel)(" unknown reason - please report!\n");
5307 return TRUE;
5310 /**********************************************************************
5311 * TranslateAcceleratorA (USER32.@)
5312 * TranslateAccelerator (USER32.@)
5314 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5316 /* YES, Accel16! */
5317 LPACCEL16 lpAccelTbl;
5318 int i;
5319 WPARAM wParam;
5321 if (!hWnd || !msg) return 0;
5323 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5325 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5326 return 0;
5329 wParam = msg->wParam;
5331 switch (msg->message)
5333 case WM_KEYDOWN:
5334 case WM_SYSKEYDOWN:
5335 break;
5337 case WM_CHAR:
5338 case WM_SYSCHAR:
5340 char ch = LOWORD(wParam);
5341 WCHAR wch;
5342 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5343 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5345 break;
5347 default:
5348 return 0;
5351 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5352 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5353 i = 0;
5356 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5357 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5358 return 1;
5359 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5361 return 0;
5364 /**********************************************************************
5365 * TranslateAcceleratorW (USER32.@)
5367 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5369 /* YES, Accel16! */
5370 LPACCEL16 lpAccelTbl;
5371 int i;
5373 if (!hWnd || !msg) return 0;
5375 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5377 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5378 return 0;
5381 switch (msg->message)
5383 case WM_KEYDOWN:
5384 case WM_SYSKEYDOWN:
5385 case WM_CHAR:
5386 case WM_SYSCHAR:
5387 break;
5389 default:
5390 return 0;
5393 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5394 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5395 i = 0;
5398 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5399 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5400 return 1;
5401 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5403 return 0;