crypt32: Make a global copy of crypt32's HINSTANCE.
[wine/multimedia.git] / dlls / user32 / menu.c
blob928a9eb47d5f0086436daf06fa26ce6aefec54f0
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 "win.h"
60 #include "controls.h"
61 #include "user_private.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(menu);
65 WINE_DECLARE_DEBUG_CHANNEL(accel);
67 /* internal popup menu window messages */
69 #define MM_SETMENUHANDLE (WM_USER + 0)
70 #define MM_GETMENUHANDLE (WM_USER + 1)
72 /* Menu item structure */
73 typedef struct {
74 /* ----------- MENUITEMINFO Stuff ----------- */
75 UINT fType; /* Item type. */
76 UINT fState; /* Item state. */
77 UINT_PTR wID; /* Item id. */
78 HMENU hSubMenu; /* Pop-up menu. */
79 HBITMAP hCheckBit; /* Bitmap when checked. */
80 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
81 LPWSTR text; /* Item text. */
82 ULONG_PTR dwItemData; /* Application defined. */
83 LPWSTR dwTypeData; /* depends on fMask */
84 HBITMAP hbmpItem; /* bitmap */
85 /* ----------- Wine stuff ----------- */
86 RECT rect; /* Item area (relative to menu window) */
87 UINT xTab; /* X position of text after Tab */
88 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
89 * bitmap */
90 } MENUITEM;
92 /* Popup menu structure */
93 typedef struct {
94 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
95 WORD wMagic; /* Magic number */
96 WORD Width; /* Width of the whole menu */
97 WORD Height; /* Height of the whole menu */
98 UINT nItems; /* Number of items in the menu */
99 HWND hWnd; /* Window containing the menu */
100 MENUITEM *items; /* Array of menu items */
101 UINT FocusedItem; /* Currently focused item */
102 HWND hwndOwner; /* window receiving the messages for ownerdraw */
103 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
104 BOOL bScrolling; /* Scroll arrows are active */
105 UINT nScrollPos; /* Current scroll position */
106 UINT nTotalHeight; /* Total height of menu items inside menu */
107 /* ------------ MENUINFO members ------ */
108 DWORD dwStyle; /* Extended menu style */
109 UINT cyMax; /* max height of the whole menu, 0 is screen height */
110 HBRUSH hbrBack; /* brush for menu background */
111 DWORD dwContextHelpID;
112 DWORD dwMenuData; /* application defined value */
113 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
114 SIZE maxBmpSize; /* Maximum size of the bitmap items */
115 } POPUPMENU, *LPPOPUPMENU;
117 /* internal flags for menu tracking */
119 #define TF_ENDMENU 0x10000
120 #define TF_SUSPENDPOPUP 0x20000
121 #define TF_SKIPREMOVE 0x40000
123 typedef struct
125 UINT trackFlags;
126 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
127 HMENU hTopMenu; /* initial menu */
128 HWND hOwnerWnd; /* where notifications are sent */
129 POINT pt;
130 } MTRACKER;
132 #define MENU_MAGIC 0x554d /* 'MU' */
134 #define ITEM_PREV -1
135 #define ITEM_NEXT 1
137 /* Internal MENU_TrackMenu() flags */
138 #define TPM_INTERNAL 0xF0000000
139 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
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 POPUPMENU* menu = MENU_GetMenu(hMenu);
422 menu->wFlags |= MF_SYSMENU | MF_POPUP;
423 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
425 else
426 ERR("Unable to load default system menu\n" );
428 TRACE("returning %p.\n", hMenu );
430 return hMenu;
434 /**********************************************************************
435 * MENU_GetSysMenu
437 * Create a copy of the system menu. System menu in Windows is
438 * a special menu bar with the single entry - system menu popup.
439 * This popup is presented to the outside world as a "system menu".
440 * However, the real system menu handle is sometimes seen in the
441 * WM_MENUSELECT parameters (and Word 6 likes it this way).
443 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
445 HMENU hMenu;
447 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
448 if ((hMenu = CreateMenu()))
450 POPUPMENU *menu = MENU_GetMenu(hMenu);
451 menu->wFlags = MF_SYSMENU;
452 menu->hWnd = WIN_GetFullHandle( hWnd );
453 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
455 if (!hPopupMenu)
456 hPopupMenu = MENU_CopySysPopup();
458 if (hPopupMenu)
460 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
461 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
463 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
464 (UINT_PTR)hPopupMenu, NULL );
466 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
467 menu->items[0].fState = 0;
468 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
470 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
471 return hMenu;
473 DestroyMenu( hMenu );
475 ERR("failed to load system menu!\n");
476 return 0;
480 /***********************************************************************
481 * MENU_InitSysMenuPopup
483 * Grey the appropriate items in System menu.
485 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
487 BOOL gray;
489 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
490 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
491 gray = ((style & WS_MAXIMIZE) != 0);
492 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
493 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
494 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
496 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
497 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
498 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
499 gray = (clsStyle & CS_NOCLOSE) != 0;
501 /* The menu item must keep its state if it's disabled */
502 if(gray)
503 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
507 /******************************************************************************
509 * UINT MENU_GetStartOfNextColumn(
510 * HMENU hMenu )
512 *****************************************************************************/
514 static UINT MENU_GetStartOfNextColumn(
515 HMENU hMenu )
517 POPUPMENU *menu = MENU_GetMenu(hMenu);
518 UINT i;
520 if(!menu)
521 return NO_SELECTED_ITEM;
523 i = menu->FocusedItem + 1;
524 if( i == NO_SELECTED_ITEM )
525 return i;
527 for( ; i < menu->nItems; ++i ) {
528 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
529 return i;
532 return NO_SELECTED_ITEM;
536 /******************************************************************************
538 * UINT MENU_GetStartOfPrevColumn(
539 * HMENU hMenu )
541 *****************************************************************************/
543 static UINT MENU_GetStartOfPrevColumn(
544 HMENU hMenu )
546 POPUPMENU *menu = MENU_GetMenu(hMenu);
547 UINT i;
549 if( !menu )
550 return NO_SELECTED_ITEM;
552 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
553 return NO_SELECTED_ITEM;
555 /* Find the start of the column */
557 for(i = menu->FocusedItem; i != 0 &&
558 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
559 --i); /* empty */
561 if(i == 0)
562 return NO_SELECTED_ITEM;
564 for(--i; i != 0; --i) {
565 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
566 break;
569 TRACE("ret %d.\n", i );
571 return i;
576 /***********************************************************************
577 * MENU_FindItem
579 * Find a menu item. Return a pointer on the item, and modifies *hmenu
580 * in case the item was in a sub-menu.
582 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
584 POPUPMENU *menu;
585 MENUITEM *fallback = NULL;
586 UINT fallback_pos = 0;
587 UINT i;
589 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
590 if (wFlags & MF_BYPOSITION)
592 if (*nPos >= menu->nItems) return NULL;
593 return &menu->items[*nPos];
595 else
597 MENUITEM *item = menu->items;
598 for (i = 0; i < menu->nItems; i++, item++)
600 if (item->fType & MF_POPUP)
602 HMENU hsubmenu = item->hSubMenu;
603 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
604 if (subitem)
606 *hmenu = hsubmenu;
607 return subitem;
609 else if (item->wID == *nPos)
611 /* fallback to this item if nothing else found */
612 fallback_pos = i;
613 fallback = item;
616 else if (item->wID == *nPos)
618 *nPos = i;
619 return item;
624 if (fallback)
625 *nPos = fallback_pos;
627 return fallback;
630 /***********************************************************************
631 * MENU_FindSubMenu
633 * Find a Sub menu. Return the position of the submenu, and modifies
634 * *hmenu in case it is found in another sub-menu.
635 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
637 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
639 POPUPMENU *menu;
640 UINT i;
641 MENUITEM *item;
642 if (((*hmenu)==(HMENU)0xffff) ||
643 (!(menu = MENU_GetMenu(*hmenu))))
644 return NO_SELECTED_ITEM;
645 item = menu->items;
646 for (i = 0; i < menu->nItems; i++, item++) {
647 if(!(item->fType & MF_POPUP)) continue;
648 if (item->hSubMenu == hSubTarget) {
649 return i;
651 else {
652 HMENU hsubmenu = item->hSubMenu;
653 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
654 if (pos != NO_SELECTED_ITEM) {
655 *hmenu = hsubmenu;
656 return pos;
660 return NO_SELECTED_ITEM;
663 /***********************************************************************
664 * MENU_FreeItemData
666 static void MENU_FreeItemData( MENUITEM* item )
668 /* delete text */
669 HeapFree( GetProcessHeap(), 0, item->text );
672 /***********************************************************************
673 * MENU_AdjustMenuItemRect
675 * Adjust menu item rectangle according to scrolling state.
677 static void
678 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
680 if (menu->bScrolling)
682 UINT arrow_bitmap_height;
683 BITMAP bmp;
685 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
686 arrow_bitmap_height = bmp.bmHeight;
687 rect->top += arrow_bitmap_height - menu->nScrollPos;
688 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
693 /***********************************************************************
694 * MENU_FindItemByCoords
696 * Find the item at the specified coordinates (screen coords). Does
697 * not work for child windows and therefore should not be called for
698 * an arbitrary system menu.
700 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
701 POINT pt, UINT *pos )
703 MENUITEM *item;
704 UINT i;
705 RECT rect;
707 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
708 pt.x -= rect.left;
709 pt.y -= rect.top;
710 item = menu->items;
711 for (i = 0; i < menu->nItems; i++, item++)
713 rect = item->rect;
714 MENU_AdjustMenuItemRect(menu, &rect);
715 if (PtInRect(&rect, pt))
717 if (pos) *pos = i;
718 return item;
721 return NULL;
725 /***********************************************************************
726 * MENU_FindItemByKey
728 * Find the menu item selected by a key press.
729 * Return item id, -1 if none, -2 if we should close the menu.
731 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
732 WCHAR key, BOOL forceMenuChar )
734 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
736 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
738 if (hmenu)
740 POPUPMENU *menu = MENU_GetMenu( hmenu );
741 MENUITEM *item = menu->items;
742 LRESULT menuchar;
744 if( !forceMenuChar )
746 UINT i;
748 for (i = 0; i < menu->nItems; i++, item++)
750 if( item->text)
752 WCHAR *p = item->text - 2;
755 p = strchrW (p + 2, '&');
757 while (p != NULL && p [1] == '&');
758 if (p && (toupperW(p[1]) == toupperW(key))) return i;
762 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
763 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
764 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
765 if (HIWORD(menuchar) == 1) return (UINT)(-2);
767 return (UINT)(-1);
771 /***********************************************************************
772 * MENU_GetBitmapItemSize
774 * Get the size of a bitmap item.
776 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
777 HWND hwndOwner)
779 BITMAP bm;
780 HBITMAP bmp = lpitem->hbmpItem;
782 size->cx = size->cy = 0;
784 /* check if there is a magic menu item associated with this item */
785 switch( (INT_PTR) bmp )
787 case (INT_PTR)HBMMENU_CALLBACK:
789 MEASUREITEMSTRUCT measItem;
790 measItem.CtlType = ODT_MENU;
791 measItem.CtlID = 0;
792 measItem.itemID = lpitem->wID;
793 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
794 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
795 measItem.itemData = lpitem->dwItemData;
796 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
797 size->cx = measItem.itemWidth;
798 size->cy = measItem.itemHeight;
799 return;
801 break;
802 case (INT_PTR)HBMMENU_SYSTEM:
803 if (lpitem->dwItemData)
805 bmp = (HBITMAP)lpitem->dwItemData;
806 break;
808 /* fall through */
809 case (INT_PTR)HBMMENU_MBAR_RESTORE:
810 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
811 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
812 case (INT_PTR)HBMMENU_MBAR_CLOSE:
813 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
814 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
815 size->cy = size->cx;
816 return;
817 case (INT_PTR)HBMMENU_POPUP_CLOSE:
818 case (INT_PTR)HBMMENU_POPUP_RESTORE:
819 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
820 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
821 FIXME("Magic %p not implemented\n", bmp );
822 return;
824 if (GetObjectW(bmp, sizeof(bm), &bm ))
826 size->cx = bm.bmWidth;
827 size->cy = bm.bmHeight;
831 /***********************************************************************
832 * MENU_DrawBitmapItem
834 * Draw a bitmap item.
836 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
837 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
839 BITMAP bm;
840 DWORD rop;
841 HDC hdcMem;
842 HBITMAP bmp;
843 int w = rect->right - rect->left;
844 int h = rect->bottom - rect->top;
845 int bmp_xoffset = 0;
846 int left, top;
847 HBITMAP hbmToDraw = lpitem->hbmpItem;
848 bmp = hbmToDraw;
850 /* Check if there is a magic menu item associated with this item */
851 if (IS_MAGIC_BITMAP(hbmToDraw))
853 UINT flags = 0;
854 RECT r;
856 switch((INT_PTR)hbmToDraw)
858 case (INT_PTR)HBMMENU_SYSTEM:
859 if (lpitem->dwItemData)
861 bmp = (HBITMAP)lpitem->dwItemData;
862 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
864 else
866 static HBITMAP hBmpSysMenu;
868 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
869 bmp = hBmpSysMenu;
870 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
871 /* only use right half of the bitmap */
872 bmp_xoffset = bm.bmWidth / 2;
873 bm.bmWidth -= bmp_xoffset;
875 goto got_bitmap;
876 case (INT_PTR)HBMMENU_MBAR_RESTORE:
877 flags = DFCS_CAPTIONRESTORE;
878 break;
879 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
880 flags = DFCS_CAPTIONMIN;
881 break;
882 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
883 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
884 break;
885 case (INT_PTR)HBMMENU_MBAR_CLOSE:
886 flags = DFCS_CAPTIONCLOSE;
887 break;
888 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
889 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
890 break;
891 case (INT_PTR)HBMMENU_CALLBACK:
893 DRAWITEMSTRUCT drawItem;
894 drawItem.CtlType = ODT_MENU;
895 drawItem.CtlID = 0;
896 drawItem.itemID = lpitem->wID;
897 drawItem.itemAction = odaction;
898 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
899 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
900 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
901 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
902 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
903 drawItem.hwndItem = (HWND)hmenu;
904 drawItem.hDC = hdc;
905 drawItem.itemData = lpitem->dwItemData;
906 drawItem.rcItem = *rect;
907 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
908 return;
910 break;
911 case (INT_PTR)HBMMENU_POPUP_CLOSE:
912 case (INT_PTR)HBMMENU_POPUP_RESTORE:
913 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
914 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
915 default:
916 FIXME("Magic %p not implemented\n", hbmToDraw);
917 return;
919 r = *rect;
920 InflateRect( &r, -1, -1 );
921 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
922 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
923 return;
926 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
928 got_bitmap:
929 hdcMem = CreateCompatibleDC( hdc );
930 SelectObject( hdcMem, bmp );
932 /* handle fontsize > bitmap_height */
933 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
934 left=rect->left;
935 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
936 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
937 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
938 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
939 DeleteDC( hdcMem );
943 /***********************************************************************
944 * MENU_CalcItemSize
946 * Calculate the size of the menu item and store it in lpitem->rect.
948 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
949 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
951 WCHAR *p;
952 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
953 UINT arrow_bitmap_width;
954 BITMAP bm;
955 INT itemheight;
957 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
958 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
959 (menuBar ? " (MenuBar)" : ""));
961 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
962 arrow_bitmap_width = bm.bmWidth;
964 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
965 if( !menucharsize.cx ) {
966 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
967 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
968 * but it is unlikely an application will depend on that */
969 ODitemheight = HIWORD( GetDialogBaseUnits());
972 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
974 if (lpitem->fType & MF_OWNERDRAW)
976 MEASUREITEMSTRUCT mis;
977 mis.CtlType = ODT_MENU;
978 mis.CtlID = 0;
979 mis.itemID = lpitem->wID;
980 mis.itemData = lpitem->dwItemData;
981 mis.itemHeight = ODitemheight;
982 mis.itemWidth = 0;
983 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
984 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
985 * width of a menufont character to the width of an owner-drawn menu.
987 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
988 if (menuBar) {
989 /* under at least win95 you seem to be given a standard
990 height for the menu and the height value is ignored */
991 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
992 } else
993 lpitem->rect.bottom += mis.itemHeight;
995 TRACE("id=%04lx size=%dx%d\n",
996 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
997 lpitem->rect.bottom-lpitem->rect.top);
998 return;
1001 if (lpitem->fType & MF_SEPARATOR)
1003 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1004 if( !menuBar)
1005 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1006 return;
1009 itemheight = 0;
1010 lpitem->xTab = 0;
1012 if (!menuBar) {
1013 if (lpitem->hbmpItem) {
1014 SIZE size;
1016 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1017 /* Keep the size of the bitmap in callback mode to be able
1018 * to draw it correctly */
1019 lpitem->bmpsize = size;
1020 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1021 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1022 lpitem->rect.right += size.cx + 2;
1023 itemheight = size.cy + 2;
1025 if( !(lppop->dwStyle & MNS_NOCHECK))
1026 lpitem->rect.right += check_bitmap_width;
1027 lpitem->rect.right += 4 + menucharsize.cx;
1028 lpitem->xTab = lpitem->rect.right;
1029 lpitem->rect.right += arrow_bitmap_width;
1030 } else if (lpitem->hbmpItem) { /* menuBar */
1031 SIZE size;
1033 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1034 lpitem->bmpsize = size;
1035 lpitem->rect.right += size.cx;
1036 if( lpitem->text) lpitem->rect.right += 2;
1037 itemheight = size.cy;
1040 /* it must be a text item - unless it's the system menu */
1041 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1042 HFONT hfontOld = NULL;
1043 RECT rc = lpitem->rect;
1044 LONG txtheight, txtwidth;
1046 if ( lpitem->fState & MFS_DEFAULT ) {
1047 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1049 if (menuBar) {
1050 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1051 DT_SINGLELINE|DT_CALCRECT);
1052 lpitem->rect.right += rc.right - rc.left;
1053 itemheight = max( max( itemheight, txtheight),
1054 GetSystemMetrics( SM_CYMENU) - 1);
1055 lpitem->rect.right += 2 * menucharsize.cx;
1056 } else {
1057 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1058 RECT tmprc = rc;
1059 LONG tmpheight;
1060 int n = (int)( p - lpitem->text);
1061 /* Item contains a tab (only meaningful in popup menus) */
1062 /* get text size before the tab */
1063 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1064 DT_SINGLELINE|DT_CALCRECT);
1065 txtwidth = rc.right - rc.left;
1066 p += 1; /* advance past the Tab */
1067 /* get text size after the tab */
1068 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1069 DT_SINGLELINE|DT_CALCRECT);
1070 lpitem->xTab += txtwidth;
1071 txtheight = max( txtheight, tmpheight);
1072 txtwidth += menucharsize.cx + /* space for the tab */
1073 tmprc.right - tmprc.left; /* space for the short cut */
1074 } else {
1075 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1076 DT_SINGLELINE|DT_CALCRECT);
1077 txtwidth = rc.right - rc.left;
1078 lpitem->xTab += txtwidth;
1080 lpitem->rect.right += 2 + txtwidth;
1081 itemheight = max( itemheight,
1082 max( txtheight + 2, menucharsize.cy + 4));
1084 if (hfontOld) SelectObject (hdc, hfontOld);
1085 } else if( menuBar) {
1086 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1088 lpitem->rect.bottom += itemheight;
1089 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1093 /***********************************************************************
1094 * MENU_GetMaxPopupHeight
1096 static UINT
1097 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1099 if (lppop->cyMax)
1100 return lppop->cyMax;
1101 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1105 /***********************************************************************
1106 * MENU_PopupMenuCalcSize
1108 * Calculate the size of a popup menu.
1110 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1112 MENUITEM *lpitem;
1113 HDC hdc;
1114 int start, i;
1115 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1117 lppop->Width = lppop->Height = 0;
1118 if (lppop->nItems == 0) return;
1119 hdc = GetDC( 0 );
1121 SelectObject( hdc, get_menu_font(FALSE));
1123 start = 0;
1124 maxX = 2 + 1;
1126 lppop->maxBmpSize.cx = 0;
1127 lppop->maxBmpSize.cy = 0;
1129 while (start < lppop->nItems)
1131 lpitem = &lppop->items[start];
1132 orgX = maxX;
1133 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1134 orgX += MENU_COL_SPACE;
1135 orgY = MENU_TOP_MARGIN;
1137 maxTab = maxTabWidth = 0;
1138 /* Parse items until column break or end of menu */
1139 for (i = start; i < lppop->nItems; i++, lpitem++)
1141 if ((i != start) &&
1142 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1144 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1145 maxX = max( maxX, lpitem->rect.right );
1146 orgY = lpitem->rect.bottom;
1147 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1149 maxTab = max( maxTab, lpitem->xTab );
1150 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1154 /* Finish the column (set all items to the largest width found) */
1155 maxX = max( maxX, maxTab + maxTabWidth );
1156 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1158 lpitem->rect.right = maxX;
1159 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1160 lpitem->xTab = maxTab;
1163 lppop->Height = max( lppop->Height, orgY );
1166 lppop->Width = maxX;
1168 /* space for 3d border */
1169 lppop->Height += MENU_BOTTOM_MARGIN;
1170 lppop->Width += 2;
1172 /* Adjust popup height if it exceeds maximum */
1173 maxHeight = MENU_GetMaxPopupHeight(lppop);
1174 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1175 if (lppop->Height >= maxHeight)
1177 lppop->Height = maxHeight;
1178 lppop->bScrolling = TRUE;
1180 else
1182 lppop->bScrolling = FALSE;
1185 ReleaseDC( 0, hdc );
1189 /***********************************************************************
1190 * MENU_MenuBarCalcSize
1192 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1193 * height is off by 1 pixel which causes lengthy window relocations when
1194 * active document window is maximized/restored.
1196 * Calculate the size of the menu bar.
1198 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1199 LPPOPUPMENU lppop, HWND hwndOwner )
1201 MENUITEM *lpitem;
1202 int start, i, orgX, orgY, maxY, helpPos;
1204 if ((lprect == NULL) || (lppop == NULL)) return;
1205 if (lppop->nItems == 0) return;
1206 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1207 lppop->Width = lprect->right - lprect->left;
1208 lppop->Height = 0;
1209 maxY = lprect->top+1;
1210 start = 0;
1211 helpPos = -1;
1212 lppop->maxBmpSize.cx = 0;
1213 lppop->maxBmpSize.cy = 0;
1214 while (start < lppop->nItems)
1216 lpitem = &lppop->items[start];
1217 orgX = lprect->left;
1218 orgY = maxY;
1220 /* Parse items until line break or end of menu */
1221 for (i = start; i < lppop->nItems; i++, lpitem++)
1223 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1224 if ((i != start) &&
1225 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1227 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1228 debug_print_menuitem (" item: ", lpitem, "");
1229 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1231 if (lpitem->rect.right > lprect->right)
1233 if (i != start) break;
1234 else lpitem->rect.right = lprect->right;
1236 maxY = max( maxY, lpitem->rect.bottom );
1237 orgX = lpitem->rect.right;
1240 /* Finish the line (set all items to the largest height found) */
1241 while (start < i) lppop->items[start++].rect.bottom = maxY;
1244 lprect->bottom = maxY;
1245 lppop->Height = lprect->bottom - lprect->top;
1247 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1248 /* the last item (if several lines, only move the last line) */
1249 lpitem = &lppop->items[lppop->nItems-1];
1250 orgY = lpitem->rect.top;
1251 orgX = lprect->right;
1252 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1253 if ( (helpPos==-1) || (helpPos>i) )
1254 break; /* done */
1255 if (lpitem->rect.top != orgY) break; /* Other line */
1256 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1257 lpitem->rect.left += orgX - lpitem->rect.right;
1258 lpitem->rect.right = orgX;
1259 orgX = lpitem->rect.left;
1264 /***********************************************************************
1265 * MENU_DrawScrollArrows
1267 * Draw scroll arrows.
1269 static void
1270 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1272 HDC hdcMem = CreateCompatibleDC(hdc);
1273 HBITMAP hOrigBitmap;
1274 UINT arrow_bitmap_width, arrow_bitmap_height;
1275 BITMAP bmp;
1276 RECT rect;
1278 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1279 arrow_bitmap_width = bmp.bmWidth;
1280 arrow_bitmap_height = bmp.bmHeight;
1283 if (lppop->nScrollPos)
1284 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1285 else
1286 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1287 rect.left = 0;
1288 rect.top = 0;
1289 rect.right = lppop->Width;
1290 rect.bottom = arrow_bitmap_height;
1291 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1292 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1293 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1294 rect.top = lppop->Height - arrow_bitmap_height;
1295 rect.bottom = lppop->Height;
1296 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1297 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1298 SelectObject(hdcMem, get_down_arrow_bitmap());
1299 else
1300 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1301 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1302 lppop->Height - arrow_bitmap_height,
1303 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1304 SelectObject(hdcMem, hOrigBitmap);
1305 DeleteDC(hdcMem);
1309 /***********************************************************************
1310 * draw_popup_arrow
1312 * Draws the popup-menu arrow.
1314 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1315 UINT arrow_bitmap_height)
1317 HDC hdcMem = CreateCompatibleDC( hdc );
1318 HBITMAP hOrigBitmap;
1320 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1321 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1322 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1323 arrow_bitmap_width, arrow_bitmap_height,
1324 hdcMem, 0, 0, SRCCOPY );
1325 SelectObject( hdcMem, hOrigBitmap );
1326 DeleteDC( hdcMem );
1328 /***********************************************************************
1329 * MENU_DrawMenuItem
1331 * Draw a single menu item.
1333 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1334 UINT height, BOOL menuBar, UINT odaction )
1336 RECT rect;
1337 BOOL flat_menu = FALSE;
1338 int bkgnd;
1339 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1340 POPUPMENU *menu = MENU_GetMenu(hmenu);
1341 RECT bmprc;
1343 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1345 if (!menuBar) {
1346 BITMAP bmp;
1347 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1348 arrow_bitmap_width = bmp.bmWidth;
1349 arrow_bitmap_height = bmp.bmHeight;
1352 if (lpitem->fType & MF_SYSMENU)
1354 if( !IsIconic(hwnd) )
1355 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1356 return;
1359 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1360 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1362 /* Setup colors */
1364 if (lpitem->fState & MF_HILITE)
1366 if(menuBar && !flat_menu) {
1367 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1368 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1369 } else {
1370 if(lpitem->fState & MF_GRAYED)
1371 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1372 else
1373 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1374 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1377 else
1379 if (lpitem->fState & MF_GRAYED)
1380 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1381 else
1382 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1383 SetBkColor( hdc, GetSysColor( bkgnd ) );
1386 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1387 rect = lpitem->rect;
1388 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1390 if (lpitem->fType & MF_OWNERDRAW)
1393 ** Experimentation under Windows reveals that an owner-drawn
1394 ** menu is given the rectangle which includes the space it requested
1395 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1396 ** and a popup-menu arrow. This is the value of lpitem->rect.
1397 ** Windows will leave all drawing to the application except for
1398 ** the popup-menu arrow. Windows always draws that itself, after
1399 ** the menu owner has finished drawing.
1401 DRAWITEMSTRUCT dis;
1403 dis.CtlType = ODT_MENU;
1404 dis.CtlID = 0;
1405 dis.itemID = lpitem->wID;
1406 dis.itemData = lpitem->dwItemData;
1407 dis.itemState = 0;
1408 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1409 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1410 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1411 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1412 dis.hwndItem = (HWND)hmenu;
1413 dis.hDC = hdc;
1414 dis.rcItem = rect;
1415 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1416 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1417 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1418 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1419 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1420 /* Draw the popup-menu arrow */
1421 if (lpitem->fType & MF_POPUP)
1422 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1423 arrow_bitmap_height);
1424 return;
1427 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1429 if (lpitem->fState & MF_HILITE)
1431 if (flat_menu)
1433 InflateRect (&rect, -1, -1);
1434 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1435 InflateRect (&rect, 1, 1);
1436 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1438 else
1440 if(menuBar)
1441 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1442 else
1443 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1446 else
1447 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1449 SetBkMode( hdc, TRANSPARENT );
1451 /* vertical separator */
1452 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1454 HPEN oldPen;
1455 RECT rc = rect;
1457 rc.left -= MENU_COL_SPACE / 2 + 1;
1458 rc.top = 3;
1459 rc.bottom = height - 3;
1460 if (flat_menu)
1462 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1463 MoveToEx( hdc, rc.left, rc.top, NULL );
1464 LineTo( hdc, rc.left, rc.bottom );
1465 SelectObject( hdc, oldPen );
1467 else
1468 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1471 /* horizontal separator */
1472 if (lpitem->fType & MF_SEPARATOR)
1474 HPEN oldPen;
1475 RECT rc = rect;
1477 rc.left++;
1478 rc.right--;
1479 rc.top = ( rc.top + rc.bottom) / 2;
1480 if (flat_menu)
1482 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1483 MoveToEx( hdc, rc.left, rc.top, NULL );
1484 LineTo( hdc, rc.right, rc.top );
1485 SelectObject( hdc, oldPen );
1487 else
1488 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1489 return;
1492 /* helper lines for debugging */
1493 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1494 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1495 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1496 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1499 if (lpitem->hbmpItem) {
1500 /* calculate the bitmap rectangle in coordinates relative
1501 * to the item rectangle */
1502 if( menuBar) {
1503 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1504 bmprc.left = 3;
1505 else
1506 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1508 else if (menu->dwStyle & MNS_NOCHECK)
1509 bmprc.left = 4;
1510 else if (menu->dwStyle & MNS_CHECKORBMP)
1511 bmprc.left = 2;
1512 else
1513 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1514 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1515 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1516 bmprc.top = 0;
1517 else
1518 bmprc.top = (rect.bottom - rect.top -
1519 lpitem->bmpsize.cy) / 2;
1520 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1523 if (!menuBar)
1525 HBITMAP bm;
1526 INT y = rect.top + rect.bottom;
1527 RECT rc = rect;
1528 int checked = FALSE;
1529 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1530 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1531 /* Draw the check mark
1533 * FIXME:
1534 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1536 if( !(menu->dwStyle & MNS_NOCHECK)) {
1537 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1538 lpitem->hUnCheckBit;
1539 if (bm) /* we have a custom bitmap */
1541 HDC hdcMem = CreateCompatibleDC( hdc );
1543 SelectObject( hdcMem, bm );
1544 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1545 check_bitmap_width, check_bitmap_height,
1546 hdcMem, 0, 0, SRCCOPY );
1547 DeleteDC( hdcMem );
1548 checked = TRUE;
1550 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1552 RECT r;
1553 HBITMAP bm = CreateBitmap( check_bitmap_width,
1554 check_bitmap_height, 1, 1, NULL );
1555 HDC hdcMem = CreateCompatibleDC( hdc );
1557 SelectObject( hdcMem, bm );
1558 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1559 DrawFrameControl( hdcMem, &r, DFC_MENU,
1560 (lpitem->fType & MFT_RADIOCHECK) ?
1561 DFCS_MENUBULLET : DFCS_MENUCHECK );
1562 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1563 hdcMem, 0, 0, SRCCOPY );
1564 DeleteDC( hdcMem );
1565 DeleteObject( bm );
1566 checked = TRUE;
1569 if( lpitem->hbmpItem &&
1570 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1571 POINT origorg;
1572 /* some applications make this assumption on the DC's origin */
1573 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1574 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1575 odaction, FALSE);
1576 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1578 /* Draw the popup-menu arrow */
1579 if (lpitem->fType & MF_POPUP)
1580 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1581 arrow_bitmap_height);
1582 rect.left += 4;
1583 if( !(menu->dwStyle & MNS_NOCHECK))
1584 rect.left += check_bitmap_width;
1585 rect.right -= arrow_bitmap_width;
1587 else if( lpitem->hbmpItem)
1588 { /* Draw the bitmap */
1589 POINT origorg;
1591 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1592 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1593 odaction, menuBar);
1594 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1596 /* process text if present */
1597 if (lpitem->text)
1599 register int i;
1600 HFONT hfontOld = 0;
1602 UINT uFormat = (menuBar) ?
1603 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1604 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1606 if( !(menu->dwStyle & MNS_CHECKORBMP))
1607 rect.left += menu->maxBmpSize.cx;
1609 if ( lpitem->fState & MFS_DEFAULT )
1611 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1614 if (menuBar) {
1615 if( lpitem->hbmpItem)
1616 rect.left += lpitem->bmpsize.cx;
1617 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1618 rect.left += menucharsize.cx;
1619 rect.right -= menucharsize.cx;
1622 for (i = 0; lpitem->text[i]; i++)
1623 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1624 break;
1626 if(lpitem->fState & MF_GRAYED)
1628 if (!(lpitem->fState & MF_HILITE) )
1630 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1631 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1632 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1633 --rect.left; --rect.top; --rect.right; --rect.bottom;
1635 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1638 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1640 /* paint the shortcut text */
1641 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1643 if (lpitem->text[i] == '\t')
1645 rect.left = lpitem->xTab;
1646 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1648 else
1650 rect.right = lpitem->xTab;
1651 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1654 if(lpitem->fState & MF_GRAYED)
1656 if (!(lpitem->fState & MF_HILITE) )
1658 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1659 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1660 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1661 --rect.left; --rect.top; --rect.right; --rect.bottom;
1663 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1665 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1668 if (hfontOld)
1669 SelectObject (hdc, hfontOld);
1674 /***********************************************************************
1675 * MENU_DrawPopupMenu
1677 * Paint a popup menu.
1679 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1681 HBRUSH hPrevBrush = 0;
1682 RECT rect;
1684 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1686 GetClientRect( hwnd, &rect );
1688 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1689 && (SelectObject( hdc, get_menu_font(FALSE))))
1691 HPEN hPrevPen;
1693 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1695 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1696 if( hPrevPen )
1698 POPUPMENU *menu;
1699 BOOL flat_menu = FALSE;
1701 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1702 if (flat_menu)
1703 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1704 else
1705 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1707 if( (menu = MENU_GetMenu( hmenu )))
1709 /* draw menu items */
1710 if( menu->nItems)
1712 MENUITEM *item;
1713 UINT u;
1715 item = menu->items;
1716 for( u = menu->nItems; u > 0; u--, item++)
1717 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1718 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1720 /* draw scroll arrows */
1721 if (menu->bScrolling)
1722 MENU_DrawScrollArrows(menu, hdc);
1724 } else
1726 SelectObject( hdc, hPrevBrush );
1731 /***********************************************************************
1732 * MENU_DrawMenuBar
1734 * Paint a menu bar. Returns the height of the menu bar.
1735 * called from [windows/nonclient.c]
1737 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1738 BOOL suppress_draw)
1740 LPPOPUPMENU lppop;
1741 HFONT hfontOld = 0;
1742 HMENU hMenu = GetMenu(hwnd);
1744 lppop = MENU_GetMenu( hMenu );
1745 if (lppop == NULL || lprect == NULL)
1747 return GetSystemMetrics(SM_CYMENU);
1750 if (suppress_draw)
1752 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1754 if (lppop->Height == 0)
1755 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1757 lprect->bottom = lprect->top + lppop->Height;
1759 if (hfontOld) SelectObject( hDC, hfontOld);
1760 return lppop->Height;
1762 else
1763 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1767 /***********************************************************************
1768 * MENU_ShowPopup
1770 * Display a popup menu.
1772 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1773 INT x, INT y, INT xanchor, INT yanchor )
1775 POPUPMENU *menu;
1776 INT width, height;
1777 POINT pt;
1778 HMONITOR monitor;
1779 MONITORINFO info;
1781 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1782 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1784 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1785 if (menu->FocusedItem != NO_SELECTED_ITEM)
1787 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1788 menu->FocusedItem = NO_SELECTED_ITEM;
1791 /* store the owner for DrawItem */
1792 menu->hwndOwner = hwndOwner;
1794 menu->nScrollPos = 0;
1795 MENU_PopupMenuCalcSize( menu );
1797 /* adjust popup menu pos so that it fits within the desktop */
1799 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1800 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1802 /* FIXME: should use item rect */
1803 pt.x = x;
1804 pt.y = y;
1805 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1806 info.cbSize = sizeof(info);
1807 GetMonitorInfoW( monitor, &info );
1809 if( flags & TPM_RIGHTALIGN ) x -= width;
1810 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1812 if( flags & TPM_BOTTOMALIGN ) y -= height;
1813 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1815 if( x + width > info.rcWork.right)
1817 if( xanchor && x >= width - xanchor )
1818 x -= width - xanchor;
1820 if( x + width > info.rcWork.right)
1821 x = info.rcWork.right - width;
1823 if( x < info.rcWork.left ) x = info.rcWork.left;
1825 if( y + height > info.rcWork.bottom)
1827 if( yanchor && y >= height + yanchor )
1828 y -= height + yanchor;
1830 if( y + height > info.rcWork.bottom)
1831 y = info.rcWork.bottom - height;
1833 if( y < info.rcWork.top ) y = info.rcWork.top;
1835 /* NOTE: In Windows, top menu popup is not owned. */
1836 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1837 WS_POPUP, x, y, width, height,
1838 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1839 (LPVOID)hmenu );
1840 if( !menu->hWnd ) return FALSE;
1841 if (!top_popup) top_popup = menu->hWnd;
1843 /* Display the window */
1845 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1846 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1847 UpdateWindow( menu->hWnd );
1848 return TRUE;
1852 /***********************************************************************
1853 * MENU_EnsureMenuItemVisible
1855 static void
1856 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1858 if (lppop->bScrolling)
1860 MENUITEM *item = &lppop->items[wIndex];
1861 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1862 UINT nOldPos = lppop->nScrollPos;
1863 RECT rc;
1864 UINT arrow_bitmap_height;
1865 BITMAP bmp;
1867 GetClientRect(lppop->hWnd, &rc);
1869 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1870 arrow_bitmap_height = bmp.bmHeight;
1872 rc.top += arrow_bitmap_height;
1873 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1875 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1876 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1879 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1880 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1881 MENU_DrawScrollArrows(lppop, hdc);
1883 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1885 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1886 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1887 MENU_DrawScrollArrows(lppop, hdc);
1893 /***********************************************************************
1894 * MENU_SelectItem
1896 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1897 BOOL sendMenuSelect, HMENU topmenu )
1899 LPPOPUPMENU lppop;
1900 HDC hdc;
1902 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1904 lppop = MENU_GetMenu( hmenu );
1905 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1907 if (lppop->FocusedItem == wIndex) return;
1908 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1909 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1910 if (!top_popup) top_popup = lppop->hWnd;
1912 SelectObject( hdc, get_menu_font(FALSE));
1914 /* Clear previous highlighted item */
1915 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1917 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1918 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1919 lppop->Height, !(lppop->wFlags & MF_POPUP),
1920 ODA_SELECT );
1923 /* Highlight new item (if any) */
1924 lppop->FocusedItem = wIndex;
1925 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1927 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1928 lppop->items[wIndex].fState |= MF_HILITE;
1929 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1930 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1931 &lppop->items[wIndex], lppop->Height,
1932 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1934 if (sendMenuSelect)
1936 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1937 SendMessageW( hwndOwner, WM_MENUSELECT,
1938 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1939 ip->fType | ip->fState |
1940 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1943 else if (sendMenuSelect) {
1944 if(topmenu){
1945 int pos;
1946 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1947 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1948 MENUITEM *ip = &ptm->items[pos];
1949 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1950 ip->fType | ip->fState |
1951 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1955 ReleaseDC( lppop->hWnd, hdc );
1959 /***********************************************************************
1960 * MENU_MoveSelection
1962 * Moves currently selected item according to the offset parameter.
1963 * If there is no selection then it should select the last item if
1964 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1966 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1968 INT i;
1969 POPUPMENU *menu;
1971 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1973 menu = MENU_GetMenu( hmenu );
1974 if ((!menu) || (!menu->items)) return;
1976 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1978 if( menu->nItems == 1 ) return; else
1979 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1980 ; i += offset)
1981 if (!(menu->items[i].fType & MF_SEPARATOR))
1983 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1984 return;
1988 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1989 i >= 0 && i < menu->nItems ; i += offset)
1990 if (!(menu->items[i].fType & MF_SEPARATOR))
1992 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1993 return;
1998 /**********************************************************************
1999 * MENU_SetItemData
2001 * Set an item's flags, id and text ptr. Called by InsertMenu() and
2002 * ModifyMenu().
2004 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
2005 LPCWSTR str )
2007 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2008 TRACE("flags=%x str=%p\n", flags, str);
2010 if (IS_STRING_ITEM(flags))
2012 LPWSTR prevText = item->text;
2013 if (!str)
2015 flags |= MF_SEPARATOR;
2016 item->text = NULL;
2018 else
2020 LPWSTR text;
2021 /* Item beginning with a backspace is a help item */
2022 if (*str == '\b')
2024 flags |= MF_HELP;
2025 str++;
2027 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2028 return FALSE;
2029 strcpyW( text, str );
2030 item->text = text;
2032 item->hbmpItem = NULL;
2033 HeapFree( GetProcessHeap(), 0, prevText );
2035 else if(( flags & MFT_BITMAP)) {
2036 item->hbmpItem = HBITMAP_32(LOWORD(str));
2037 /* setting bitmap clears text */
2038 HeapFree( GetProcessHeap(), 0, item->text );
2039 item->text = NULL;
2042 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2044 if (flags & MF_OWNERDRAW)
2045 item->dwItemData = (DWORD_PTR)str;
2046 else
2047 item->dwItemData = 0;
2049 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2050 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2052 if (flags & MF_POPUP)
2054 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2055 if (menu) menu->wFlags |= MF_POPUP;
2056 else
2058 item->wID = 0;
2059 item->hSubMenu = 0;
2060 item->fType = 0;
2061 item->fState = 0;
2062 return FALSE;
2066 item->wID = id;
2067 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2069 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2070 flags |= MF_POPUP; /* keep popup */
2072 item->fType = flags & TYPE_MASK;
2073 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2074 for ModifyMenu, but Windows accepts it */
2075 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2077 /* Don't call SetRectEmpty here! */
2079 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2080 return TRUE;
2084 /**********************************************************************
2085 * MENU_InsertItem
2087 * Insert (allocate) a new item into a menu.
2089 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2091 MENUITEM *newItems;
2092 POPUPMENU *menu;
2094 if (!(menu = MENU_GetMenu(hMenu)))
2095 return NULL;
2097 /* Find where to insert new item */
2099 if (flags & MF_BYPOSITION) {
2100 if (pos > menu->nItems)
2101 pos = menu->nItems;
2102 } else {
2103 if (!MENU_FindItem( &hMenu, &pos, flags ))
2104 pos = menu->nItems;
2105 else {
2106 if (!(menu = MENU_GetMenu( hMenu )))
2107 return NULL;
2111 /* Make sure that MDI system buttons stay on the right side.
2112 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2113 * regardless of their id.
2115 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2116 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2117 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2118 pos--;
2120 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2122 /* Create new items array */
2124 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2125 if (!newItems)
2127 WARN("allocation failed\n" );
2128 return NULL;
2130 if (menu->nItems > 0)
2132 /* Copy the old array into the new one */
2133 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2134 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2135 (menu->nItems-pos)*sizeof(MENUITEM) );
2136 HeapFree( GetProcessHeap(), 0, menu->items );
2138 menu->items = newItems;
2139 menu->nItems++;
2140 memset( &newItems[pos], 0, sizeof(*newItems) );
2141 menu->Height = 0; /* force size recalculate */
2142 return &newItems[pos];
2146 /**********************************************************************
2147 * MENU_ParseResource
2149 * Parse a standard menu resource and add items to the menu.
2150 * Return a pointer to the end of the resource.
2152 * NOTE: flags is equivalent to the mtOption field
2154 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2156 WORD flags, id = 0;
2157 LPCSTR str;
2158 BOOL end_flag;
2162 flags = GET_WORD(res);
2163 end_flag = flags & MF_END;
2164 /* Remove MF_END because it has the same value as MF_HILITE */
2165 flags &= ~MF_END;
2166 res += sizeof(WORD);
2167 if (!(flags & MF_POPUP))
2169 id = GET_WORD(res);
2170 res += sizeof(WORD);
2172 str = res;
2173 if (!unicode) res += strlen(str) + 1;
2174 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2175 if (flags & MF_POPUP)
2177 HMENU hSubMenu = CreatePopupMenu();
2178 if (!hSubMenu) return NULL;
2179 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2180 return NULL;
2181 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2182 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2184 else /* Not a popup */
2186 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2187 else AppendMenuW( hMenu, flags, id,
2188 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2190 } while (!end_flag);
2191 return res;
2195 /**********************************************************************
2196 * MENUEX_ParseResource
2198 * Parse an extended menu resource and add items to the menu.
2199 * Return a pointer to the end of the resource.
2201 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2203 WORD resinfo;
2204 do {
2205 MENUITEMINFOW mii;
2207 mii.cbSize = sizeof(mii);
2208 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2209 mii.fType = GET_DWORD(res);
2210 res += sizeof(DWORD);
2211 mii.fState = GET_DWORD(res);
2212 res += sizeof(DWORD);
2213 mii.wID = GET_DWORD(res);
2214 res += sizeof(DWORD);
2215 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2216 res += sizeof(WORD);
2217 /* Align the text on a word boundary. */
2218 res += (~((UINT_PTR)res - 1)) & 1;
2219 mii.dwTypeData = (LPWSTR) res;
2220 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2221 /* Align the following fields on a dword boundary. */
2222 res += (~((UINT_PTR)res - 1)) & 3;
2224 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2225 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2227 if (resinfo & 1) { /* Pop-up? */
2228 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2229 res += sizeof(DWORD);
2230 mii.hSubMenu = CreatePopupMenu();
2231 if (!mii.hSubMenu)
2232 return NULL;
2233 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2234 DestroyMenu(mii.hSubMenu);
2235 return NULL;
2237 mii.fMask |= MIIM_SUBMENU;
2238 mii.fType |= MF_POPUP;
2240 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2242 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2243 mii.wID, mii.fType);
2244 mii.fType |= MF_SEPARATOR;
2246 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2247 } while (!(resinfo & MF_END));
2248 return res;
2252 /***********************************************************************
2253 * MENU_GetSubPopup
2255 * Return the handle of the selected sub-popup menu (if any).
2257 static HMENU MENU_GetSubPopup( HMENU hmenu )
2259 POPUPMENU *menu;
2260 MENUITEM *item;
2262 menu = MENU_GetMenu( hmenu );
2264 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2266 item = &menu->items[menu->FocusedItem];
2267 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2268 return item->hSubMenu;
2269 return 0;
2273 /***********************************************************************
2274 * MENU_HideSubPopups
2276 * Hide the sub-popup menus of this menu.
2278 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2279 BOOL sendMenuSelect, UINT wFlags )
2281 POPUPMENU *menu = MENU_GetMenu( hmenu );
2283 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2285 if (menu && top_popup)
2287 HMENU hsubmenu;
2288 POPUPMENU *submenu;
2289 MENUITEM *item;
2291 if (menu->FocusedItem != NO_SELECTED_ITEM)
2293 item = &menu->items[menu->FocusedItem];
2294 if (!(item->fType & MF_POPUP) ||
2295 !(item->fState & MF_MOUSESELECT)) return;
2296 item->fState &= ~MF_MOUSESELECT;
2297 hsubmenu = item->hSubMenu;
2298 } else return;
2300 submenu = MENU_GetMenu( hsubmenu );
2301 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2302 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2303 DestroyWindow( submenu->hWnd );
2304 submenu->hWnd = 0;
2306 if (!(wFlags & TPM_NONOTIFY))
2307 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2308 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2313 /***********************************************************************
2314 * MENU_ShowSubPopup
2316 * Display the sub-menu of the selected item of this menu.
2317 * Return the handle of the submenu, or hmenu if no submenu to display.
2319 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2320 BOOL selectFirst, UINT wFlags )
2322 RECT rect;
2323 POPUPMENU *menu;
2324 MENUITEM *item;
2325 HDC hdc;
2327 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2329 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2331 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2333 item = &menu->items[menu->FocusedItem];
2334 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2335 return hmenu;
2337 /* message must be sent before using item,
2338 because nearly everything may be changed by the application ! */
2340 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2341 if (!(wFlags & TPM_NONOTIFY))
2342 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2343 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2345 item = &menu->items[menu->FocusedItem];
2346 rect = item->rect;
2348 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2349 if (!(item->fState & MF_HILITE))
2351 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2352 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2354 SelectObject( hdc, get_menu_font(FALSE));
2356 item->fState |= MF_HILITE;
2357 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2358 ReleaseDC( menu->hWnd, hdc );
2360 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2361 item->rect = rect;
2363 item->fState |= MF_MOUSESELECT;
2365 if (IS_SYSTEM_MENU(menu))
2367 MENU_InitSysMenuPopup(item->hSubMenu,
2368 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2369 GetClassLongW( menu->hWnd, GCL_STYLE));
2371 NC_GetSysPopupPos( menu->hWnd, &rect );
2372 rect.top = rect.bottom;
2373 rect.right = GetSystemMetrics(SM_CXSIZE);
2374 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2376 else
2378 GetWindowRect( menu->hWnd, &rect );
2379 if (menu->wFlags & MF_POPUP)
2381 RECT rc = item->rect;
2383 MENU_AdjustMenuItemRect(menu, &rc);
2385 /* The first item in the popup menu has to be at the
2386 same y position as the focused menu item */
2387 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2388 rect.top += rc.top - MENU_TOP_MARGIN;
2389 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2390 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2391 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2393 else
2395 rect.left += item->rect.left;
2396 rect.top += item->rect.bottom;
2397 rect.right = item->rect.right - item->rect.left;
2398 rect.bottom = item->rect.bottom - item->rect.top;
2402 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2403 rect.left, rect.top, rect.right, rect.bottom );
2404 if (selectFirst)
2405 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2406 return item->hSubMenu;
2411 /**********************************************************************
2412 * MENU_IsMenuActive
2414 HWND MENU_IsMenuActive(void)
2416 return top_popup;
2419 /***********************************************************************
2420 * MENU_PtMenu
2422 * Walks menu chain trying to find a menu pt maps to.
2424 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2426 POPUPMENU *menu = MENU_GetMenu( hMenu );
2427 UINT item = menu->FocusedItem;
2428 HMENU ret;
2430 /* try subpopup first (if any) */
2431 ret = (item != NO_SELECTED_ITEM &&
2432 (menu->items[item].fType & MF_POPUP) &&
2433 (menu->items[item].fState & MF_MOUSESELECT))
2434 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2436 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2438 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2439 if( menu->wFlags & MF_POPUP )
2441 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2443 else if (ht == HTSYSMENU)
2444 ret = get_win_sys_menu( menu->hWnd );
2445 else if (ht == HTMENU)
2446 ret = GetMenu( menu->hWnd );
2448 return ret;
2451 /***********************************************************************
2452 * MENU_ExecFocusedItem
2454 * Execute a menu item (for instance when user pressed Enter).
2455 * Return the wID of the executed item. Otherwise, -1 indicating
2456 * that no menu item was executed, -2 if a popup is shown;
2457 * Have to receive the flags for the TrackPopupMenu options to avoid
2458 * sending unwanted message.
2461 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2463 MENUITEM *item;
2464 POPUPMENU *menu = MENU_GetMenu( hMenu );
2466 TRACE("%p hmenu=%p\n", pmt, hMenu);
2468 if (!menu || !menu->nItems ||
2469 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2471 item = &menu->items[menu->FocusedItem];
2473 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2475 if (!(item->fType & MF_POPUP))
2477 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2479 /* If TPM_RETURNCMD is set you return the id, but
2480 do not send a message to the owner */
2481 if(!(wFlags & TPM_RETURNCMD))
2483 if( menu->wFlags & MF_SYSMENU )
2484 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2485 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2486 else
2488 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2489 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2491 if (dwStyle & MNS_NOTIFYBYPOS)
2492 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2493 (LPARAM)hMenu);
2494 else
2495 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2498 return item->wID;
2501 else
2503 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2504 return -2;
2507 return -1;
2510 /***********************************************************************
2511 * MENU_SwitchTracking
2513 * Helper function for menu navigation routines.
2515 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2517 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2518 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2520 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2522 if( pmt->hTopMenu != hPtMenu &&
2523 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2525 /* both are top level menus (system and menu-bar) */
2526 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2527 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2528 pmt->hTopMenu = hPtMenu;
2530 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2531 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2535 /***********************************************************************
2536 * MENU_ButtonDown
2538 * Return TRUE if we can go on with menu tracking.
2540 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2542 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2544 if (hPtMenu)
2546 UINT id = 0;
2547 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2548 MENUITEM *item;
2550 if( IS_SYSTEM_MENU(ptmenu) )
2551 item = ptmenu->items;
2552 else
2553 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2555 if( item )
2557 if( ptmenu->FocusedItem != id )
2558 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2560 /* If the popup menu is not already "popped" */
2561 if(!(item->fState & MF_MOUSESELECT ))
2563 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2566 return TRUE;
2568 /* Else the click was on the menu bar, finish the tracking */
2570 return FALSE;
2573 /***********************************************************************
2574 * MENU_ButtonUp
2576 * Return the value of MENU_ExecFocusedItem if
2577 * the selected item was not a popup. Else open the popup.
2578 * A -1 return value indicates that we go on with menu tracking.
2581 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2583 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2585 if (hPtMenu)
2587 UINT id = 0;
2588 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2589 MENUITEM *item;
2591 if( IS_SYSTEM_MENU(ptmenu) )
2592 item = ptmenu->items;
2593 else
2594 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2596 if( item && (ptmenu->FocusedItem == id ))
2598 debug_print_menuitem ("FocusedItem: ", item, "");
2600 if( !(item->fType & MF_POPUP) )
2602 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2603 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2604 return executedMenuId;
2607 /* If we are dealing with the top-level menu */
2608 /* and this is a click on an already "popped" item: */
2609 /* Stop the menu tracking and close the opened submenus */
2610 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2611 return 0;
2613 ptmenu->bTimeToHide = TRUE;
2615 return -1;
2619 /***********************************************************************
2620 * MENU_MouseMove
2622 * Return TRUE if we can go on with menu tracking.
2624 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2626 UINT id = NO_SELECTED_ITEM;
2627 POPUPMENU *ptmenu = NULL;
2629 if( hPtMenu )
2631 ptmenu = MENU_GetMenu( hPtMenu );
2632 if( IS_SYSTEM_MENU(ptmenu) )
2633 id = 0;
2634 else
2635 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2638 if( id == NO_SELECTED_ITEM )
2640 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2641 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2644 else if( ptmenu->FocusedItem != id )
2646 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2647 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2649 return TRUE;
2653 /***********************************************************************
2654 * MENU_DoNextMenu
2656 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2658 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2660 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2661 BOOL atEnd = FALSE;
2663 /* When skipping left, we need to do something special after the
2664 first menu. */
2665 if (vk == VK_LEFT && menu->FocusedItem == 0)
2667 atEnd = TRUE;
2669 /* When skipping right, for the non-system menu, we need to
2670 handle the last non-special menu item (ie skip any window
2671 icons such as MDI maximize, restore or close) */
2672 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2674 int i = menu->FocusedItem + 1;
2675 while (i < menu->nItems) {
2676 if ((menu->items[i].wID >= SC_SIZE &&
2677 menu->items[i].wID <= SC_RESTORE)) {
2678 i++;
2679 } else break;
2681 if (i == menu->nItems) {
2682 atEnd = TRUE;
2685 /* When skipping right, we need to cater for the system menu */
2686 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2688 if (menu->FocusedItem == (menu->nItems - 1)) {
2689 atEnd = TRUE;
2693 if( atEnd )
2695 MDINEXTMENU next_menu;
2696 HMENU hNewMenu;
2697 HWND hNewWnd;
2698 UINT id = 0;
2700 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2701 next_menu.hmenuNext = 0;
2702 next_menu.hwndNext = 0;
2703 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2705 TRACE("%p [%p] -> %p [%p]\n",
2706 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2708 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2710 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2711 hNewWnd = pmt->hOwnerWnd;
2712 if( IS_SYSTEM_MENU(menu) )
2714 /* switch to the menu bar */
2716 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2718 if( vk == VK_LEFT )
2720 menu = MENU_GetMenu( hNewMenu );
2721 id = menu->nItems - 1;
2723 /* Skip backwards over any system predefined icons,
2724 eg. MDI close, restore etc icons */
2725 while ((id > 0) &&
2726 (menu->items[id].wID >= SC_SIZE &&
2727 menu->items[id].wID <= SC_RESTORE)) id--;
2730 else if (style & WS_SYSMENU )
2732 /* switch to the system menu */
2733 hNewMenu = get_win_sys_menu( hNewWnd );
2735 else return FALSE;
2737 else /* application returned a new menu to switch to */
2739 hNewMenu = next_menu.hmenuNext;
2740 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2742 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2744 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2746 if (style & WS_SYSMENU &&
2747 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2749 /* get the real system menu */
2750 hNewMenu = get_win_sys_menu(hNewWnd);
2752 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2754 /* FIXME: Not sure what to do here;
2755 * perhaps try to track hNewMenu as a popup? */
2757 TRACE(" -- got confused.\n");
2758 return FALSE;
2761 else return FALSE;
2764 if( hNewMenu != pmt->hTopMenu )
2766 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2767 FALSE, 0 );
2768 if( pmt->hCurrentMenu != pmt->hTopMenu )
2769 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2772 if( hNewWnd != pmt->hOwnerWnd )
2774 pmt->hOwnerWnd = hNewWnd;
2775 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2778 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2779 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2781 return TRUE;
2783 return FALSE;
2786 /***********************************************************************
2787 * MENU_SuspendPopup
2789 * The idea is not to show the popup if the next input message is
2790 * going to hide it anyway.
2792 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2794 MSG msg;
2796 msg.hwnd = pmt->hOwnerWnd;
2798 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2799 pmt->trackFlags |= TF_SKIPREMOVE;
2801 switch( uMsg )
2803 case WM_KEYDOWN:
2804 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2805 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2807 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2808 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2809 if( msg.message == WM_KEYDOWN &&
2810 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2812 pmt->trackFlags |= TF_SUSPENDPOPUP;
2813 return TRUE;
2816 break;
2819 /* failures go through this */
2820 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2821 return FALSE;
2824 /***********************************************************************
2825 * MENU_KeyEscape
2827 * Handle a VK_ESCAPE key event in a menu.
2829 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2831 BOOL bEndMenu = TRUE;
2833 if (pmt->hCurrentMenu != pmt->hTopMenu)
2835 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2837 if (menu->wFlags & MF_POPUP)
2839 HMENU hmenutmp, hmenuprev;
2841 hmenuprev = hmenutmp = pmt->hTopMenu;
2843 /* close topmost popup */
2844 while (hmenutmp != pmt->hCurrentMenu)
2846 hmenuprev = hmenutmp;
2847 hmenutmp = MENU_GetSubPopup( hmenuprev );
2850 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2851 pmt->hCurrentMenu = hmenuprev;
2852 bEndMenu = FALSE;
2856 return bEndMenu;
2859 /***********************************************************************
2860 * MENU_KeyLeft
2862 * Handle a VK_LEFT key event in a menu.
2864 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2866 POPUPMENU *menu;
2867 HMENU hmenutmp, hmenuprev;
2868 UINT prevcol;
2870 hmenuprev = hmenutmp = pmt->hTopMenu;
2871 menu = MENU_GetMenu( hmenutmp );
2873 /* Try to move 1 column left (if possible) */
2874 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2875 NO_SELECTED_ITEM ) {
2877 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2878 prevcol, TRUE, 0 );
2879 return;
2882 /* close topmost popup */
2883 while (hmenutmp != pmt->hCurrentMenu)
2885 hmenuprev = hmenutmp;
2886 hmenutmp = MENU_GetSubPopup( hmenuprev );
2889 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2890 pmt->hCurrentMenu = hmenuprev;
2892 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2894 /* move menu bar selection if no more popups are left */
2896 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2897 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2899 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2901 /* A sublevel menu was displayed - display the next one
2902 * unless there is another displacement coming up */
2904 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2905 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2906 pmt->hTopMenu, TRUE, wFlags);
2912 /***********************************************************************
2913 * MENU_KeyRight
2915 * Handle a VK_RIGHT key event in a menu.
2917 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2919 HMENU hmenutmp;
2920 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2921 UINT nextcol;
2923 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2924 pmt->hCurrentMenu,
2925 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2926 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2928 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2930 /* If already displaying a popup, try to display sub-popup */
2932 hmenutmp = pmt->hCurrentMenu;
2933 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2935 /* if subpopup was displayed then we are done */
2936 if (hmenutmp != pmt->hCurrentMenu) return;
2939 /* Check to see if there's another column */
2940 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2941 NO_SELECTED_ITEM ) {
2942 TRACE("Going to %d.\n", nextcol );
2943 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2944 nextcol, TRUE, 0 );
2945 return;
2948 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2950 if( pmt->hCurrentMenu != pmt->hTopMenu )
2952 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2953 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2954 } else hmenutmp = 0;
2956 /* try to move to the next item */
2957 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2958 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2960 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2961 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2962 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2963 pmt->hTopMenu, TRUE, wFlags);
2967 /***********************************************************************
2968 * MENU_TrackMenu
2970 * Menu tracking code.
2972 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2973 HWND hwnd, const RECT *lprect )
2975 MSG msg;
2976 POPUPMENU *menu;
2977 BOOL fRemove;
2978 INT executedMenuId = -1;
2979 MTRACKER mt;
2980 BOOL enterIdleSent = FALSE;
2981 HWND capture_win;
2983 mt.trackFlags = 0;
2984 mt.hCurrentMenu = hmenu;
2985 mt.hTopMenu = hmenu;
2986 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2987 mt.pt.x = x;
2988 mt.pt.y = y;
2990 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2991 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2993 fEndMenu = FALSE;
2994 if (!(menu = MENU_GetMenu( hmenu )))
2996 WARN("Invalid menu handle %p\n", hmenu);
2997 SetLastError(ERROR_INVALID_MENU_HANDLE);
2998 return FALSE;
3001 if (wFlags & TPM_BUTTONDOWN)
3003 /* Get the result in order to start the tracking or not */
3004 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3005 fEndMenu = !fRemove;
3008 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3010 /* owner may not be visible when tracking a popup, so use the menu itself */
3011 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3012 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3014 while (!fEndMenu)
3016 menu = MENU_GetMenu( mt.hCurrentMenu );
3017 if (!menu) /* sometimes happens if I do a window manager close */
3018 break;
3020 /* we have to keep the message in the queue until it's
3021 * clear that menu loop is not over yet. */
3023 for (;;)
3025 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3027 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3028 /* remove the message from the queue */
3029 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3031 else
3033 if (!enterIdleSent)
3035 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3036 enterIdleSent = TRUE;
3037 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3039 WaitMessage();
3043 /* check if EndMenu() tried to cancel us, by posting this message */
3044 if(msg.message == WM_CANCELMODE)
3046 /* we are now out of the loop */
3047 fEndMenu = TRUE;
3049 /* remove the message from the queue */
3050 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3052 /* break out of internal loop, ala ESCAPE */
3053 break;
3056 TranslateMessage( &msg );
3057 mt.pt = msg.pt;
3059 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3060 enterIdleSent=FALSE;
3062 fRemove = FALSE;
3063 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3066 * Use the mouse coordinates in lParam instead of those in the MSG
3067 * struct to properly handle synthetic messages. They are already
3068 * in screen coordinates.
3070 mt.pt.x = (short)LOWORD(msg.lParam);
3071 mt.pt.y = (short)HIWORD(msg.lParam);
3073 /* Find a menu for this mouse event */
3074 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3076 switch(msg.message)
3078 /* no WM_NC... messages in captured state */
3080 case WM_RBUTTONDBLCLK:
3081 case WM_RBUTTONDOWN:
3082 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3083 /* fall through */
3084 case WM_LBUTTONDBLCLK:
3085 case WM_LBUTTONDOWN:
3086 /* If the message belongs to the menu, removes it from the queue */
3087 /* Else, end menu tracking */
3088 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3089 fEndMenu = !fRemove;
3090 break;
3092 case WM_RBUTTONUP:
3093 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3094 /* fall through */
3095 case WM_LBUTTONUP:
3096 /* Check if a menu was selected by the mouse */
3097 if (hmenu)
3099 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3100 TRACE("executedMenuId %d\n", executedMenuId);
3102 /* End the loop if executedMenuId is an item ID */
3103 /* or if the job was done (executedMenuId = 0). */
3104 fEndMenu = fRemove = (executedMenuId != -1);
3106 /* No menu was selected by the mouse */
3107 /* if the function was called by TrackPopupMenu, continue
3108 with the menu tracking. If not, stop it */
3109 else
3110 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3112 break;
3114 case WM_MOUSEMOVE:
3115 /* the selected menu item must be changed every time */
3116 /* the mouse moves. */
3118 if (hmenu)
3119 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3121 } /* switch(msg.message) - mouse */
3123 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3125 fRemove = TRUE; /* Keyboard messages are always removed */
3126 switch(msg.message)
3128 case WM_KEYDOWN:
3129 case WM_SYSKEYDOWN:
3130 switch(msg.wParam)
3132 case VK_MENU:
3133 fEndMenu = TRUE;
3134 break;
3136 case VK_HOME:
3137 case VK_END:
3138 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3139 NO_SELECTED_ITEM, FALSE, 0 );
3140 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3141 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3142 break;
3144 case VK_UP:
3145 case VK_DOWN: /* If on menu bar, pull-down the menu */
3147 menu = MENU_GetMenu( mt.hCurrentMenu );
3148 if (!(menu->wFlags & MF_POPUP))
3149 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3150 else /* otherwise try to move selection */
3151 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3152 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3153 break;
3155 case VK_LEFT:
3156 MENU_KeyLeft( &mt, wFlags );
3157 break;
3159 case VK_RIGHT:
3160 MENU_KeyRight( &mt, wFlags );
3161 break;
3163 case VK_ESCAPE:
3164 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3165 break;
3167 case VK_F1:
3169 HELPINFO hi;
3170 hi.cbSize = sizeof(HELPINFO);
3171 hi.iContextType = HELPINFO_MENUITEM;
3172 if (menu->FocusedItem == NO_SELECTED_ITEM)
3173 hi.iCtrlId = 0;
3174 else
3175 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3176 hi.hItemHandle = hmenu;
3177 hi.dwContextId = menu->dwContextHelpID;
3178 hi.MousePos = msg.pt;
3179 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3180 break;
3183 default:
3184 break;
3186 break; /* WM_KEYDOWN */
3188 case WM_CHAR:
3189 case WM_SYSCHAR:
3191 UINT pos;
3193 if (msg.wParam == '\r' || msg.wParam == ' ')
3195 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3196 fEndMenu = (executedMenuId != -2);
3198 break;
3201 /* Hack to avoid control chars. */
3202 /* We will find a better way real soon... */
3203 if (msg.wParam < 32) break;
3205 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3206 LOWORD(msg.wParam), FALSE );
3207 if (pos == (UINT)-2) fEndMenu = TRUE;
3208 else if (pos == (UINT)-1) MessageBeep(0);
3209 else
3211 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3212 TRUE, 0 );
3213 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3214 fEndMenu = (executedMenuId != -2);
3217 break;
3218 } /* switch(msg.message) - kbd */
3220 else
3222 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3223 DispatchMessageW( &msg );
3224 continue;
3227 if (!fEndMenu) fRemove = TRUE;
3229 /* finally remove message from the queue */
3231 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3232 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3233 else mt.trackFlags &= ~TF_SKIPREMOVE;
3236 set_capture_window( 0, GUI_INMENUMODE, NULL );
3238 /* If dropdown is still painted and the close box is clicked on
3239 then the menu will be destroyed as part of the DispatchMessage above.
3240 This will then invalidate the menu handle in mt.hTopMenu. We should
3241 check for this first. */
3242 if( IsMenu( mt.hTopMenu ) )
3244 menu = MENU_GetMenu( mt.hTopMenu );
3246 if( IsWindow( mt.hOwnerWnd ) )
3248 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3250 if (menu && (menu->wFlags & MF_POPUP))
3252 DestroyWindow( menu->hWnd );
3253 menu->hWnd = 0;
3255 if (!(wFlags & TPM_NONOTIFY))
3256 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3257 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3259 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3260 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3263 /* Reset the variable for hiding menu */
3264 if( menu ) menu->bTimeToHide = FALSE;
3267 /* The return value is only used by TrackPopupMenu */
3268 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3269 if (executedMenuId == -1) executedMenuId = 0;
3270 return executedMenuId;
3273 /***********************************************************************
3274 * MENU_InitTracking
3276 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3278 POPUPMENU *menu;
3280 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3282 HideCaret(0);
3284 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3285 if (!(wFlags & TPM_NONOTIFY))
3286 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3288 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3290 if (!(wFlags & TPM_NONOTIFY))
3292 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3293 /* If an app changed/recreated menu bar entries in WM_INITMENU
3294 * menu sizes will be recalculated once the menu created/shown.
3298 /* This makes the menus of applications built with Delphi work.
3299 * It also enables menus to be displayed in more than one window,
3300 * but there are some bugs left that need to be fixed in this case.
3302 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3304 return TRUE;
3306 /***********************************************************************
3307 * MENU_ExitTracking
3309 static BOOL MENU_ExitTracking(HWND hWnd)
3311 TRACE("hwnd=%p\n", hWnd);
3313 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3314 ShowCaret(0);
3315 top_popup = 0;
3316 return TRUE;
3319 /***********************************************************************
3320 * MENU_TrackMouseMenuBar
3322 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3324 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3326 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3327 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3329 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3331 if (IsMenu(hMenu))
3333 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3334 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3335 MENU_ExitTracking(hWnd);
3340 /***********************************************************************
3341 * MENU_TrackKbdMenuBar
3343 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3345 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3347 UINT uItem = NO_SELECTED_ITEM;
3348 HMENU hTrackMenu;
3349 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3351 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3353 /* find window that has a menu */
3355 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3356 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3358 /* check if we have to track a system menu */
3360 hTrackMenu = GetMenu( hwnd );
3361 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3363 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3364 hTrackMenu = get_win_sys_menu( hwnd );
3365 uItem = 0;
3366 wParam |= HTSYSMENU; /* prevent item lookup */
3369 if (!IsMenu( hTrackMenu )) return;
3371 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3373 if( wChar && wChar != ' ' )
3375 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3376 if ( uItem >= (UINT)(-2) )
3378 if( uItem == (UINT)(-1) ) MessageBeep(0);
3379 /* schedule end of menu tracking */
3380 wFlags |= TF_ENDMENU;
3381 goto track_menu;
3385 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3387 if (!(wParam & HTSYSMENU) || wChar == ' ')
3389 if( uItem == NO_SELECTED_ITEM )
3390 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3391 else
3392 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3395 track_menu:
3396 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3397 MENU_ExitTracking( hwnd );
3401 /**********************************************************************
3402 * TrackPopupMenu (USER32.@)
3404 * Like the win32 API, the function return the command ID only if the
3405 * flag TPM_RETURNCMD is on.
3408 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3409 INT nReserved, HWND hWnd, const RECT *lpRect )
3411 BOOL ret = FALSE;
3413 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3414 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3416 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3418 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3419 if (!(wFlags & TPM_NONOTIFY))
3420 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3422 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3423 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3424 MENU_ExitTracking(hWnd);
3426 return ret;
3429 /**********************************************************************
3430 * TrackPopupMenuEx (USER32.@)
3432 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3433 HWND hWnd, LPTPMPARAMS lpTpm )
3435 FIXME("not fully implemented\n" );
3436 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3437 lpTpm ? &lpTpm->rcExclude : NULL );
3440 /***********************************************************************
3441 * PopupMenuWndProc
3443 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3445 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3447 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3449 switch(message)
3451 case WM_CREATE:
3453 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3454 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3455 return 0;
3458 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3459 return MA_NOACTIVATE;
3461 case WM_PAINT:
3463 PAINTSTRUCT ps;
3464 BeginPaint( hwnd, &ps );
3465 MENU_DrawPopupMenu( hwnd, ps.hdc,
3466 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3467 EndPaint( hwnd, &ps );
3468 return 0;
3470 case WM_ERASEBKGND:
3471 return 1;
3473 case WM_DESTROY:
3474 /* zero out global pointer in case resident popup window was destroyed. */
3475 if (hwnd == top_popup) top_popup = 0;
3476 break;
3478 case WM_SHOWWINDOW:
3480 if( wParam )
3482 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3484 else
3485 SetWindowLongPtrW( hwnd, 0, 0 );
3486 break;
3488 case MM_SETMENUHANDLE:
3489 SetWindowLongPtrW( hwnd, 0, wParam );
3490 break;
3492 case MM_GETMENUHANDLE:
3493 return GetWindowLongPtrW( hwnd, 0 );
3495 default:
3496 return DefWindowProcW( hwnd, message, wParam, lParam );
3498 return 0;
3502 /***********************************************************************
3503 * MENU_GetMenuBarHeight
3505 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3507 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3508 INT orgX, INT orgY )
3510 HDC hdc;
3511 RECT rectBar;
3512 LPPOPUPMENU lppop;
3514 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3516 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3518 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3519 SelectObject( hdc, get_menu_font(FALSE));
3520 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3521 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3522 ReleaseDC( hwnd, hdc );
3523 return lppop->Height;
3527 /*******************************************************************
3528 * ChangeMenuA (USER32.@)
3530 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3531 UINT id, UINT flags )
3533 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3534 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3535 id, data );
3536 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3537 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3538 id, data );
3539 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3540 flags & MF_BYPOSITION ? pos : id,
3541 flags & ~MF_REMOVE );
3542 /* Default: MF_INSERT */
3543 return InsertMenuA( hMenu, pos, flags, id, data );
3547 /*******************************************************************
3548 * ChangeMenuW (USER32.@)
3550 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3551 UINT id, UINT flags )
3553 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3554 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3555 id, data );
3556 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3557 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3558 id, data );
3559 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3560 flags & MF_BYPOSITION ? pos : id,
3561 flags & ~MF_REMOVE );
3562 /* Default: MF_INSERT */
3563 return InsertMenuW( hMenu, pos, flags, id, data );
3567 /*******************************************************************
3568 * CheckMenuItem (USER32.@)
3570 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3572 MENUITEM *item;
3573 DWORD ret;
3575 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3576 ret = item->fState & MF_CHECKED;
3577 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3578 else item->fState &= ~MF_CHECKED;
3579 return ret;
3583 /**********************************************************************
3584 * EnableMenuItem (USER32.@)
3586 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3588 UINT oldflags;
3589 MENUITEM *item;
3590 POPUPMENU *menu;
3592 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3594 /* Get the Popupmenu to access the owner menu */
3595 if (!(menu = MENU_GetMenu(hMenu)))
3596 return (UINT)-1;
3598 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3599 return (UINT)-1;
3601 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3602 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3604 /* If the close item in the system menu change update the close button */
3605 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3607 if (menu->hSysMenuOwner != 0)
3609 RECT rc;
3610 POPUPMENU* parentMenu;
3612 /* Get the parent menu to access*/
3613 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3614 return (UINT)-1;
3616 /* Refresh the frame to reflect the change */
3617 GetWindowRect(parentMenu->hWnd, &rc);
3618 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3619 rc.bottom = 0;
3620 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3624 return oldflags;
3628 /*******************************************************************
3629 * GetMenuStringA (USER32.@)
3631 INT WINAPI GetMenuStringA(
3632 HMENU hMenu, /* [in] menuhandle */
3633 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3634 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3635 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3636 UINT wFlags /* [in] MF_ flags */
3638 MENUITEM *item;
3640 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3641 if (str && nMaxSiz) str[0] = '\0';
3642 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3643 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3644 return 0;
3646 if (!item->text) return 0;
3647 if (!str || !nMaxSiz) return strlenW(item->text);
3648 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3649 str[nMaxSiz-1] = 0;
3650 TRACE("returning %s\n", debugstr_a(str));
3651 return strlen(str);
3655 /*******************************************************************
3656 * GetMenuStringW (USER32.@)
3658 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3659 LPWSTR str, INT nMaxSiz, UINT wFlags )
3661 MENUITEM *item;
3663 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3664 if (str && nMaxSiz) str[0] = '\0';
3665 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3666 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3667 return 0;
3669 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3670 if( !(item->text)) {
3671 str[0] = 0;
3672 return 0;
3674 lstrcpynW( str, item->text, nMaxSiz );
3675 TRACE("returning %s\n", debugstr_w(str));
3676 return strlenW(str);
3680 /**********************************************************************
3681 * HiliteMenuItem (USER32.@)
3683 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3684 UINT wHilite )
3686 LPPOPUPMENU menu;
3687 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3688 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3689 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3690 if (menu->FocusedItem == wItemID) return TRUE;
3691 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3692 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3693 return TRUE;
3697 /**********************************************************************
3698 * GetMenuState (USER32.@)
3700 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3702 MENUITEM *item;
3703 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3704 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3705 debug_print_menuitem (" item: ", item, "");
3706 if (item->fType & MF_POPUP)
3708 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3709 if (!menu) return -1;
3710 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3712 else
3714 /* We used to (from way back then) mask the result to 0xff. */
3715 /* I don't know why and it seems wrong as the documented */
3716 /* return flag MF_SEPARATOR is outside that mask. */
3717 return (item->fType | item->fState);
3722 /**********************************************************************
3723 * GetMenuItemCount (USER32.@)
3725 INT WINAPI GetMenuItemCount( HMENU hMenu )
3727 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3728 if (!menu) return -1;
3729 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3730 return menu->nItems;
3734 /**********************************************************************
3735 * GetMenuItemID (USER32.@)
3737 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3739 MENUITEM * lpmi;
3741 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3742 if (lpmi->fType & MF_POPUP) return -1;
3743 return lpmi->wID;
3748 /*******************************************************************
3749 * InsertMenuW (USER32.@)
3751 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3752 UINT_PTR id, LPCWSTR str )
3754 MENUITEM *item;
3756 if (IS_STRING_ITEM(flags) && str)
3757 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3758 hMenu, pos, flags, id, debugstr_w(str) );
3759 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3760 hMenu, pos, flags, id, str );
3762 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3764 if (!(MENU_SetItemData( item, flags, id, str )))
3766 RemoveMenu( hMenu, pos, flags );
3767 return FALSE;
3770 item->hCheckBit = item->hUnCheckBit = 0;
3771 return TRUE;
3775 /*******************************************************************
3776 * InsertMenuA (USER32.@)
3778 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3779 UINT_PTR id, LPCSTR str )
3781 BOOL ret = FALSE;
3783 if (IS_STRING_ITEM(flags) && str)
3785 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3786 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3787 if (newstr)
3789 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3790 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3791 HeapFree( GetProcessHeap(), 0, newstr );
3793 return ret;
3795 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3799 /*******************************************************************
3800 * AppendMenuA (USER32.@)
3802 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3803 UINT_PTR id, LPCSTR data )
3805 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3809 /*******************************************************************
3810 * AppendMenuW (USER32.@)
3812 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3813 UINT_PTR id, LPCWSTR data )
3815 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3819 /**********************************************************************
3820 * RemoveMenu (USER32.@)
3822 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3824 LPPOPUPMENU menu;
3825 MENUITEM *item;
3827 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3828 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3829 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3831 /* Remove item */
3833 MENU_FreeItemData( item );
3835 if (--menu->nItems == 0)
3837 HeapFree( GetProcessHeap(), 0, menu->items );
3838 menu->items = NULL;
3840 else
3842 while(nPos < menu->nItems)
3844 *item = *(item+1);
3845 item++;
3846 nPos++;
3848 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3849 menu->nItems * sizeof(MENUITEM) );
3851 return TRUE;
3855 /**********************************************************************
3856 * DeleteMenu (USER32.@)
3858 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3860 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3861 if (!item) return FALSE;
3862 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3863 /* nPos is now the position of the item */
3864 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3865 return TRUE;
3869 /*******************************************************************
3870 * ModifyMenuW (USER32.@)
3872 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3873 UINT_PTR id, LPCWSTR str )
3875 MENUITEM *item;
3877 if (IS_STRING_ITEM(flags))
3878 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3879 else
3880 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3882 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3883 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3884 return MENU_SetItemData( item, flags, id, str );
3888 /*******************************************************************
3889 * ModifyMenuA (USER32.@)
3891 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3892 UINT_PTR id, LPCSTR str )
3894 BOOL ret = FALSE;
3896 if (IS_STRING_ITEM(flags) && str)
3898 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3899 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3900 if (newstr)
3902 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3903 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3904 HeapFree( GetProcessHeap(), 0, newstr );
3906 return ret;
3908 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3912 /**********************************************************************
3913 * CreatePopupMenu (USER32.@)
3915 HMENU WINAPI CreatePopupMenu(void)
3917 HMENU hmenu;
3918 POPUPMENU *menu;
3920 if (!(hmenu = CreateMenu())) return 0;
3921 menu = MENU_GetMenu( hmenu );
3922 menu->wFlags |= MF_POPUP;
3923 menu->bTimeToHide = FALSE;
3924 return hmenu;
3928 /**********************************************************************
3929 * GetMenuCheckMarkDimensions (USER.417)
3930 * GetMenuCheckMarkDimensions (USER32.@)
3932 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3934 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3938 /**********************************************************************
3939 * SetMenuItemBitmaps (USER32.@)
3941 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3942 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3944 MENUITEM *item;
3946 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3948 if (!hNewCheck && !hNewUnCheck)
3950 item->fState &= ~MF_USECHECKBITMAPS;
3952 else /* Install new bitmaps */
3954 item->hCheckBit = hNewCheck;
3955 item->hUnCheckBit = hNewUnCheck;
3956 item->fState |= MF_USECHECKBITMAPS;
3958 return TRUE;
3962 /**********************************************************************
3963 * CreateMenu (USER32.@)
3965 HMENU WINAPI CreateMenu(void)
3967 HMENU hMenu;
3968 LPPOPUPMENU menu;
3969 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3970 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3972 ZeroMemory(menu, sizeof(POPUPMENU));
3973 menu->wMagic = MENU_MAGIC;
3974 menu->FocusedItem = NO_SELECTED_ITEM;
3975 menu->bTimeToHide = FALSE;
3977 TRACE("return %p\n", hMenu );
3979 return hMenu;
3983 /**********************************************************************
3984 * DestroyMenu (USER32.@)
3986 BOOL WINAPI DestroyMenu( HMENU hMenu )
3988 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3990 TRACE("(%p)\n", hMenu);
3993 if (!lppop) return FALSE;
3995 lppop->wMagic = 0; /* Mark it as destroyed */
3997 /* DestroyMenu should not destroy system menu popup owner */
3998 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4000 DestroyWindow( lppop->hWnd );
4001 lppop->hWnd = 0;
4004 if (lppop->items) /* recursively destroy submenus */
4006 int i;
4007 MENUITEM *item = lppop->items;
4008 for (i = lppop->nItems; i > 0; i--, item++)
4010 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4011 MENU_FreeItemData( item );
4013 HeapFree( GetProcessHeap(), 0, lppop->items );
4015 USER_HEAP_FREE( hMenu );
4016 return TRUE;
4020 /**********************************************************************
4021 * GetSystemMenu (USER32.@)
4023 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4025 WND *wndPtr = WIN_GetPtr( hWnd );
4026 HMENU retvalue = 0;
4028 if (wndPtr == WND_DESKTOP) return 0;
4029 if (wndPtr == WND_OTHER_PROCESS)
4031 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4033 else if (wndPtr)
4035 if (wndPtr->hSysMenu && bRevert)
4037 DestroyMenu(wndPtr->hSysMenu);
4038 wndPtr->hSysMenu = 0;
4041 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4042 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4044 if( wndPtr->hSysMenu )
4046 POPUPMENU *menu;
4047 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4049 /* Store the dummy sysmenu handle to facilitate the refresh */
4050 /* of the close button if the SC_CLOSE item change */
4051 menu = MENU_GetMenu(retvalue);
4052 if ( menu )
4053 menu->hSysMenuOwner = wndPtr->hSysMenu;
4055 WIN_ReleasePtr( wndPtr );
4057 return bRevert ? 0 : retvalue;
4061 /*******************************************************************
4062 * SetSystemMenu (USER32.@)
4064 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4066 WND *wndPtr = WIN_GetPtr( hwnd );
4068 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4070 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4071 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4072 WIN_ReleasePtr( wndPtr );
4073 return TRUE;
4075 return FALSE;
4079 /**********************************************************************
4080 * GetMenu (USER32.@)
4082 HMENU WINAPI GetMenu( HWND hWnd )
4084 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4085 TRACE("for %p returning %p\n", hWnd, retvalue);
4086 return retvalue;
4089 /**********************************************************************
4090 * GetMenuBarInfo (USER32.@)
4092 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4094 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4095 return FALSE;
4098 /**********************************************************************
4099 * MENU_SetMenu
4101 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4102 * SetWindowPos call that would result if SetMenu were called directly.
4104 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4106 TRACE("(%p, %p);\n", hWnd, hMenu);
4108 if (hMenu && !IsMenu(hMenu))
4110 WARN("hMenu %p is not a menu handle\n", hMenu);
4111 return FALSE;
4113 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4114 return FALSE;
4116 hWnd = WIN_GetFullHandle( hWnd );
4117 if (GetCapture() == hWnd)
4118 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4120 if (hMenu != 0)
4122 LPPOPUPMENU lpmenu;
4124 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4126 lpmenu->hWnd = hWnd;
4127 lpmenu->Height = 0; /* Make sure we recalculate the size */
4129 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4130 return TRUE;
4134 /**********************************************************************
4135 * SetMenu (USER32.@)
4137 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4139 if(!MENU_SetMenu(hWnd, hMenu))
4140 return FALSE;
4142 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4143 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4144 return TRUE;
4148 /**********************************************************************
4149 * GetSubMenu (USER32.@)
4151 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4153 MENUITEM * lpmi;
4155 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4156 if (!(lpmi->fType & MF_POPUP)) return 0;
4157 return lpmi->hSubMenu;
4161 /**********************************************************************
4162 * DrawMenuBar (USER32.@)
4164 BOOL WINAPI DrawMenuBar( HWND hWnd )
4166 LPPOPUPMENU lppop;
4167 HMENU hMenu = GetMenu(hWnd);
4169 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4170 return FALSE;
4171 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4173 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4174 lppop->hwndOwner = hWnd;
4175 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4176 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4177 return TRUE;
4180 /***********************************************************************
4181 * DrawMenuBarTemp (USER32.@)
4183 * UNDOCUMENTED !!
4185 * called by W98SE desk.cpl Control Panel Applet
4187 * Not 100% sure about the param names, but close.
4189 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4191 LPPOPUPMENU lppop;
4192 UINT i,retvalue;
4193 HFONT hfontOld = 0;
4194 BOOL flat_menu = FALSE;
4196 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4198 if (!hMenu)
4199 hMenu = GetMenu(hwnd);
4201 if (!hFont)
4202 hFont = get_menu_font(FALSE);
4204 lppop = MENU_GetMenu( hMenu );
4205 if (lppop == NULL || lprect == NULL)
4207 retvalue = GetSystemMetrics(SM_CYMENU);
4208 goto END;
4211 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4213 hfontOld = SelectObject( hDC, hFont);
4215 if (lppop->Height == 0)
4216 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4218 lprect->bottom = lprect->top + lppop->Height;
4220 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4222 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4223 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4224 LineTo( hDC, lprect->right, lprect->bottom );
4226 if (lppop->nItems == 0)
4228 retvalue = GetSystemMetrics(SM_CYMENU);
4229 goto END;
4232 for (i = 0; i < lppop->nItems; i++)
4234 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4235 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4237 retvalue = lppop->Height;
4239 END:
4240 if (hfontOld) SelectObject (hDC, hfontOld);
4241 return retvalue;
4244 /***********************************************************************
4245 * EndMenu (USER.187)
4246 * EndMenu (USER32.@)
4248 BOOL WINAPI EndMenu(void)
4250 /* if we are in the menu code, and it is active */
4251 if (!fEndMenu && top_popup)
4253 /* terminate the menu handling code */
4254 fEndMenu = TRUE;
4256 /* needs to be posted to wakeup the internal menu handler */
4257 /* which will now terminate the menu, in the event that */
4258 /* the main window was minimized, or lost focus, so we */
4259 /* don't end up with an orphaned menu */
4260 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4262 return fEndMenu;
4266 /***********************************************************************
4267 * LookupMenuHandle (USER.217)
4269 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4271 HMENU hmenu32 = HMENU_32(hmenu);
4272 UINT id32 = id;
4273 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4274 else return HMENU_16(hmenu32);
4278 /**********************************************************************
4279 * LoadMenu (USER.150)
4281 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4283 HRSRC16 hRsrc;
4284 HGLOBAL16 handle;
4285 HMENU16 hMenu;
4287 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4288 if (!name) return 0;
4290 instance = GetExePtr( instance );
4291 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4292 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4293 hMenu = LoadMenuIndirect16(LockResource16(handle));
4294 FreeResource16( handle );
4295 return hMenu;
4299 /*****************************************************************
4300 * LoadMenuA (USER32.@)
4302 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4304 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4305 if (!hrsrc) return 0;
4306 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4310 /*****************************************************************
4311 * LoadMenuW (USER32.@)
4313 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4315 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4316 if (!hrsrc) return 0;
4317 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4321 /**********************************************************************
4322 * LoadMenuIndirect (USER.220)
4324 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4326 HMENU hMenu;
4327 WORD version, offset;
4328 LPCSTR p = (LPCSTR)template;
4330 TRACE("(%p)\n", template );
4331 version = GET_WORD(p);
4332 p += sizeof(WORD);
4333 if (version)
4335 WARN("version must be 0 for Win16\n" );
4336 return 0;
4338 offset = GET_WORD(p);
4339 p += sizeof(WORD) + offset;
4340 if (!(hMenu = CreateMenu())) return 0;
4341 if (!MENU_ParseResource( p, hMenu, FALSE ))
4343 DestroyMenu( hMenu );
4344 return 0;
4346 return HMENU_16(hMenu);
4350 /**********************************************************************
4351 * LoadMenuIndirectW (USER32.@)
4353 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4355 HMENU hMenu;
4356 WORD version, offset;
4357 LPCSTR p = (LPCSTR)template;
4359 version = GET_WORD(p);
4360 p += sizeof(WORD);
4361 TRACE("%p, ver %d\n", template, version );
4362 switch (version)
4364 case 0: /* standard format is version of 0 */
4365 offset = GET_WORD(p);
4366 p += sizeof(WORD) + offset;
4367 if (!(hMenu = CreateMenu())) return 0;
4368 if (!MENU_ParseResource( p, hMenu, TRUE ))
4370 DestroyMenu( hMenu );
4371 return 0;
4373 return hMenu;
4374 case 1: /* extended format is version of 1 */
4375 offset = GET_WORD(p);
4376 p += sizeof(WORD) + offset;
4377 if (!(hMenu = CreateMenu())) return 0;
4378 if (!MENUEX_ParseResource( p, hMenu))
4380 DestroyMenu( hMenu );
4381 return 0;
4383 return hMenu;
4384 default:
4385 ERR("version %d not supported.\n", version);
4386 return 0;
4391 /**********************************************************************
4392 * LoadMenuIndirectA (USER32.@)
4394 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4396 return LoadMenuIndirectW( template );
4400 /**********************************************************************
4401 * IsMenu (USER32.@)
4403 BOOL WINAPI IsMenu(HMENU hmenu)
4405 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4407 if (!menu)
4409 SetLastError(ERROR_INVALID_MENU_HANDLE);
4410 return FALSE;
4412 return TRUE;
4415 /**********************************************************************
4416 * GetMenuItemInfo_common
4419 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4420 LPMENUITEMINFOW lpmii, BOOL unicode)
4422 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4424 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4426 if (!menu)
4427 return FALSE;
4429 if( lpmii->fMask & MIIM_TYPE) {
4430 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4431 WARN("invalid combination of fMask bits used\n");
4432 /* this does not happen on Win9x/ME */
4433 SetLastError( ERROR_INVALID_PARAMETER);
4434 return FALSE;
4436 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4437 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4438 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4439 if( lpmii->fType & MFT_BITMAP) {
4440 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4441 lpmii->cch = 0;
4442 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4443 /* this does not happen on Win9x/ME */
4444 lpmii->dwTypeData = 0;
4445 lpmii->cch = 0;
4449 /* copy the text string */
4450 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4451 if( !menu->text ) {
4452 if(lpmii->dwTypeData && lpmii->cch) {
4453 lpmii->cch = 0;
4454 if( unicode)
4455 *((WCHAR *)lpmii->dwTypeData) = 0;
4456 else
4457 *((CHAR *)lpmii->dwTypeData) = 0;
4459 } else {
4460 int len;
4461 if (unicode)
4463 len = strlenW(menu->text);
4464 if(lpmii->dwTypeData && lpmii->cch)
4465 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4467 else
4469 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4470 0, NULL, NULL ) - 1;
4471 if(lpmii->dwTypeData && lpmii->cch)
4472 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4473 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4474 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4476 /* if we've copied a substring we return its length */
4477 if(lpmii->dwTypeData && lpmii->cch)
4478 if (lpmii->cch <= len + 1)
4479 lpmii->cch--;
4480 else
4481 lpmii->cch = len;
4482 else {
4483 /* return length of string */
4484 /* not on Win9x/ME if fType & MFT_BITMAP */
4485 lpmii->cch = len;
4490 if (lpmii->fMask & MIIM_FTYPE)
4491 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4493 if (lpmii->fMask & MIIM_BITMAP)
4494 lpmii->hbmpItem = menu->hbmpItem;
4496 if (lpmii->fMask & MIIM_STATE)
4497 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4499 if (lpmii->fMask & MIIM_ID)
4500 lpmii->wID = menu->wID;
4502 if (lpmii->fMask & MIIM_SUBMENU)
4503 lpmii->hSubMenu = menu->hSubMenu;
4504 else {
4505 /* hSubMenu is always cleared
4506 * (not on Win9x/ME ) */
4507 lpmii->hSubMenu = 0;
4510 if (lpmii->fMask & MIIM_CHECKMARKS) {
4511 lpmii->hbmpChecked = menu->hCheckBit;
4512 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4514 if (lpmii->fMask & MIIM_DATA)
4515 lpmii->dwItemData = menu->dwItemData;
4517 return TRUE;
4520 /**********************************************************************
4521 * GetMenuItemInfoA (USER32.@)
4523 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4524 LPMENUITEMINFOA lpmii)
4526 BOOL ret;
4527 MENUITEMINFOA mii;
4528 if( lpmii->cbSize != sizeof( mii) &&
4529 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4530 SetLastError( ERROR_INVALID_PARAMETER);
4531 return FALSE;
4533 memcpy( &mii, lpmii, lpmii->cbSize);
4534 mii.cbSize = sizeof( mii);
4535 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4536 (LPMENUITEMINFOW)&mii, FALSE);
4537 mii.cbSize = lpmii->cbSize;
4538 memcpy( lpmii, &mii, mii.cbSize);
4539 return ret;
4542 /**********************************************************************
4543 * GetMenuItemInfoW (USER32.@)
4545 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4546 LPMENUITEMINFOW lpmii)
4548 BOOL ret;
4549 MENUITEMINFOW mii;
4550 if( lpmii->cbSize != sizeof( mii) &&
4551 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4552 SetLastError( ERROR_INVALID_PARAMETER);
4553 return FALSE;
4555 memcpy( &mii, lpmii, lpmii->cbSize);
4556 mii.cbSize = sizeof( mii);
4557 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4558 mii.cbSize = lpmii->cbSize;
4559 memcpy( lpmii, &mii, mii.cbSize);
4560 return ret;
4564 /* set a menu item text from a ASCII or Unicode string */
4565 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4567 if (!text)
4568 menu->text = NULL;
4569 else if (unicode)
4571 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4572 strcpyW( menu->text, text );
4574 else
4576 LPCSTR str = (LPCSTR)text;
4577 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4578 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4579 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4584 /**********************************************************************
4585 * SetMenuItemInfo_common
4588 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4589 const MENUITEMINFOW *lpmii,
4590 BOOL unicode)
4592 if (!menu) return FALSE;
4594 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4596 if (lpmii->fMask & MIIM_TYPE ) {
4597 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4598 WARN("invalid combination of fMask bits used\n");
4599 /* this does not happen on Win9x/ME */
4600 SetLastError( ERROR_INVALID_PARAMETER);
4601 return FALSE;
4604 /* Remove the old type bits and replace them with the new ones */
4605 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4606 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4608 if (IS_STRING_ITEM(menu->fType)) {
4609 HeapFree(GetProcessHeap(), 0, menu->text);
4610 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4611 } else if( (menu->fType) & MFT_BITMAP)
4612 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4615 if (lpmii->fMask & MIIM_FTYPE ) {
4616 if(( lpmii->fType & MFT_BITMAP)) {
4617 SetLastError( ERROR_INVALID_PARAMETER);
4618 return FALSE;
4620 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4621 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4623 if (lpmii->fMask & MIIM_STRING ) {
4624 /* free the string when used */
4625 HeapFree(GetProcessHeap(), 0, menu->text);
4626 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4629 if (lpmii->fMask & MIIM_STATE)
4631 /* Other menu items having MFS_DEFAULT are not converted
4632 to normal items */
4633 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4636 if (lpmii->fMask & MIIM_ID)
4637 menu->wID = lpmii->wID;
4639 if (lpmii->fMask & MIIM_SUBMENU) {
4640 menu->hSubMenu = lpmii->hSubMenu;
4641 if (menu->hSubMenu) {
4642 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4643 if (subMenu) {
4644 subMenu->wFlags |= MF_POPUP;
4645 menu->fType |= MF_POPUP;
4647 else {
4648 SetLastError( ERROR_INVALID_PARAMETER);
4649 return FALSE;
4652 else
4653 menu->fType &= ~MF_POPUP;
4656 if (lpmii->fMask & MIIM_CHECKMARKS)
4658 menu->hCheckBit = lpmii->hbmpChecked;
4659 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4661 if (lpmii->fMask & MIIM_DATA)
4662 menu->dwItemData = lpmii->dwItemData;
4664 if (lpmii->fMask & MIIM_BITMAP)
4665 menu->hbmpItem = lpmii->hbmpItem;
4667 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4668 menu->fType |= MFT_SEPARATOR;
4670 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4671 return TRUE;
4674 /**********************************************************************
4675 * SetMenuItemInfoA (USER32.@)
4677 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4678 const MENUITEMINFOA *lpmii)
4680 MENUITEMINFOA mii;
4682 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4684 if( lpmii->cbSize != sizeof( mii) &&
4685 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4686 SetLastError( ERROR_INVALID_PARAMETER);
4687 return FALSE;
4689 memcpy( &mii, lpmii, lpmii->cbSize);
4690 if( lpmii->cbSize != sizeof( mii)) {
4691 mii.cbSize = sizeof( mii);
4692 mii.hbmpItem = NULL;
4694 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4695 (const MENUITEMINFOW *)&mii, FALSE);
4698 /**********************************************************************
4699 * SetMenuItemInfoW (USER32.@)
4701 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4702 const MENUITEMINFOW *lpmii)
4704 MENUITEMINFOW mii;
4706 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4708 if( lpmii->cbSize != sizeof( mii) &&
4709 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4710 SetLastError( ERROR_INVALID_PARAMETER);
4711 return FALSE;
4713 memcpy( &mii, lpmii, lpmii->cbSize);
4714 if( lpmii->cbSize != sizeof( mii)) {
4715 mii.cbSize = sizeof( mii);
4716 mii.hbmpItem = NULL;
4718 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4719 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4722 /**********************************************************************
4723 * SetMenuDefaultItem (USER32.@)
4726 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4728 UINT i;
4729 POPUPMENU *menu;
4730 MENUITEM *item;
4732 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4734 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4736 /* reset all default-item flags */
4737 item = menu->items;
4738 for (i = 0; i < menu->nItems; i++, item++)
4740 item->fState &= ~MFS_DEFAULT;
4743 /* no default item */
4744 if ( -1 == uItem)
4746 return TRUE;
4749 item = menu->items;
4750 if ( bypos )
4752 if ( uItem >= menu->nItems ) return FALSE;
4753 item[uItem].fState |= MFS_DEFAULT;
4754 return TRUE;
4756 else
4758 for (i = 0; i < menu->nItems; i++, item++)
4760 if (item->wID == uItem)
4762 item->fState |= MFS_DEFAULT;
4763 return TRUE;
4768 return FALSE;
4771 /**********************************************************************
4772 * GetMenuDefaultItem (USER32.@)
4774 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4776 POPUPMENU *menu;
4777 MENUITEM * item;
4778 UINT i = 0;
4780 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4782 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4784 /* find default item */
4785 item = menu->items;
4787 /* empty menu */
4788 if (! item) return -1;
4790 while ( !( item->fState & MFS_DEFAULT ) )
4792 i++; item++;
4793 if (i >= menu->nItems ) return -1;
4796 /* default: don't return disabled items */
4797 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4799 /* search rekursiv when needed */
4800 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4802 UINT ret;
4803 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4804 if ( -1 != ret ) return ret;
4806 /* when item not found in submenu, return the popup item */
4808 return ( bypos ) ? i : item->wID;
4813 /**********************************************************************
4814 * InsertMenuItemA (USER32.@)
4816 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4817 const MENUITEMINFOA *lpmii)
4819 MENUITEM *item;
4820 MENUITEMINFOA mii;
4822 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4824 if( lpmii->cbSize != sizeof( mii) &&
4825 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4826 SetLastError( ERROR_INVALID_PARAMETER);
4827 return FALSE;
4829 memcpy( &mii, lpmii, lpmii->cbSize);
4830 if( lpmii->cbSize != sizeof( mii)) {
4831 mii.cbSize = sizeof( mii);
4832 mii.hbmpItem = NULL;
4835 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4836 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4840 /**********************************************************************
4841 * InsertMenuItemW (USER32.@)
4843 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4844 const MENUITEMINFOW *lpmii)
4846 MENUITEM *item;
4847 MENUITEMINFOW mii;
4849 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4851 if( lpmii->cbSize != sizeof( mii) &&
4852 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4853 SetLastError( ERROR_INVALID_PARAMETER);
4854 return FALSE;
4856 memcpy( &mii, lpmii, lpmii->cbSize);
4857 if( lpmii->cbSize != sizeof( mii)) {
4858 mii.cbSize = sizeof( mii);
4859 mii.hbmpItem = NULL;
4862 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4863 return SetMenuItemInfo_common(item, &mii, TRUE);
4866 /**********************************************************************
4867 * CheckMenuRadioItem (USER32.@)
4870 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4871 UINT first, UINT last, UINT check,
4872 UINT bypos)
4874 BOOL done = FALSE;
4875 UINT i;
4876 MENUITEM *mi_first = NULL, *mi_check;
4877 HMENU m_first, m_check;
4879 for (i = first; i <= last; i++)
4881 UINT pos = i;
4883 if (!mi_first)
4885 m_first = hMenu;
4886 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4887 if (!mi_first) continue;
4888 mi_check = mi_first;
4889 m_check = m_first;
4891 else
4893 m_check = hMenu;
4894 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4895 if (!mi_check) continue;
4898 if (m_first != m_check) continue;
4899 if (mi_check->fType == MFT_SEPARATOR) continue;
4901 if (i == check)
4903 mi_check->fType |= MFT_RADIOCHECK;
4904 mi_check->fState |= MFS_CHECKED;
4905 done = TRUE;
4907 else
4909 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4910 mi_check->fState &= ~MFS_CHECKED;
4914 return done;
4918 /**********************************************************************
4919 * GetMenuItemRect (USER32.@)
4921 * ATTENTION: Here, the returned values in rect are the screen
4922 * coordinates of the item just like if the menu was
4923 * always on the upper left side of the application.
4926 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4927 LPRECT rect)
4929 POPUPMENU *itemMenu;
4930 MENUITEM *item;
4931 HWND referenceHwnd;
4933 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4935 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4936 referenceHwnd = hwnd;
4938 if(!hwnd)
4940 itemMenu = MENU_GetMenu(hMenu);
4941 if (itemMenu == NULL)
4942 return FALSE;
4944 if(itemMenu->hWnd == 0)
4945 return FALSE;
4946 referenceHwnd = itemMenu->hWnd;
4949 if ((rect == NULL) || (item == NULL))
4950 return FALSE;
4952 *rect = item->rect;
4954 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4956 return TRUE;
4960 /**********************************************************************
4961 * SetMenuInfo (USER32.@)
4963 * FIXME
4964 * MIM_APPLYTOSUBMENUS
4965 * actually use the items to draw the menu
4967 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4969 POPUPMENU *menu;
4971 TRACE("(%p %p)\n", hMenu, lpmi);
4973 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4976 if (lpmi->fMask & MIM_BACKGROUND)
4977 menu->hbrBack = lpmi->hbrBack;
4979 if (lpmi->fMask & MIM_HELPID)
4980 menu->dwContextHelpID = lpmi->dwContextHelpID;
4982 if (lpmi->fMask & MIM_MAXHEIGHT)
4983 menu->cyMax = lpmi->cyMax;
4985 if (lpmi->fMask & MIM_MENUDATA)
4986 menu->dwMenuData = lpmi->dwMenuData;
4988 if (lpmi->fMask & MIM_STYLE)
4990 menu->dwStyle = lpmi->dwStyle;
4991 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4992 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4993 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4996 return TRUE;
4998 return FALSE;
5001 /**********************************************************************
5002 * GetMenuInfo (USER32.@)
5004 * NOTES
5005 * win98/NT5.0
5008 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5009 { POPUPMENU *menu;
5011 TRACE("(%p %p)\n", hMenu, lpmi);
5013 if (lpmi && (menu = MENU_GetMenu(hMenu)))
5016 if (lpmi->fMask & MIM_BACKGROUND)
5017 lpmi->hbrBack = menu->hbrBack;
5019 if (lpmi->fMask & MIM_HELPID)
5020 lpmi->dwContextHelpID = menu->dwContextHelpID;
5022 if (lpmi->fMask & MIM_MAXHEIGHT)
5023 lpmi->cyMax = menu->cyMax;
5025 if (lpmi->fMask & MIM_MENUDATA)
5026 lpmi->dwMenuData = menu->dwMenuData;
5028 if (lpmi->fMask & MIM_STYLE)
5029 lpmi->dwStyle = menu->dwStyle;
5031 return TRUE;
5033 return FALSE;
5037 /**********************************************************************
5038 * SetMenuContextHelpId (USER32.@)
5040 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5042 LPPOPUPMENU menu;
5044 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5046 if ((menu = MENU_GetMenu(hMenu)))
5048 menu->dwContextHelpID = dwContextHelpID;
5049 return TRUE;
5051 return FALSE;
5055 /**********************************************************************
5056 * GetMenuContextHelpId (USER32.@)
5058 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5060 LPPOPUPMENU menu;
5062 TRACE("(%p)\n", hMenu);
5064 if ((menu = MENU_GetMenu(hMenu)))
5066 return menu->dwContextHelpID;
5068 return 0;
5071 /**********************************************************************
5072 * MenuItemFromPoint (USER32.@)
5074 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5076 POPUPMENU *menu = MENU_GetMenu(hMenu);
5077 UINT pos;
5079 /*FIXME: Do we have to handle hWnd here? */
5080 if (!menu) return -1;
5081 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5082 return pos;
5086 /**********************************************************************
5087 * translate_accelerator
5089 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5090 BYTE fVirt, WORD key, WORD cmd )
5092 INT mask = 0;
5093 UINT mesg = 0;
5095 if (wParam != key) return FALSE;
5097 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5098 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5099 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5101 if (message == WM_CHAR || message == WM_SYSCHAR)
5103 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5105 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5106 goto found;
5109 else
5111 if(fVirt & FVIRTKEY)
5113 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5114 wParam, 0xff & HIWORD(lParam));
5116 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5117 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5119 else
5121 if (!(lParam & 0x01000000)) /* no special_key */
5123 if ((fVirt & FALT) && (lParam & 0x20000000))
5124 { /* ^^ ALT pressed */
5125 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5126 goto found;
5131 return FALSE;
5133 found:
5134 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5135 mesg = 1;
5136 else
5138 HMENU hMenu, hSubMenu, hSysMenu;
5139 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5141 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5142 hSysMenu = get_win_sys_menu( hWnd );
5144 /* find menu item and ask application to initialize it */
5145 /* 1. in the system menu */
5146 hSubMenu = hSysMenu;
5147 nPos = cmd;
5148 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5150 if (GetCapture())
5151 mesg = 2;
5152 if (!IsWindowEnabled(hWnd))
5153 mesg = 3;
5154 else
5156 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5157 if(hSubMenu != hSysMenu)
5159 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5160 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5161 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5163 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5166 else /* 2. in the window's menu */
5168 hSubMenu = hMenu;
5169 nPos = cmd;
5170 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5172 if (GetCapture())
5173 mesg = 2;
5174 if (!IsWindowEnabled(hWnd))
5175 mesg = 3;
5176 else
5178 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5179 if(hSubMenu != hMenu)
5181 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5182 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5183 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5185 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5190 if (mesg == 0)
5192 if (uSysStat != (UINT)-1)
5194 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5195 mesg=4;
5196 else
5197 mesg=WM_SYSCOMMAND;
5199 else
5201 if (uStat != (UINT)-1)
5203 if (IsIconic(hWnd))
5204 mesg=5;
5205 else
5207 if (uStat & (MF_DISABLED|MF_GRAYED))
5208 mesg=6;
5209 else
5210 mesg=WM_COMMAND;
5213 else
5214 mesg=WM_COMMAND;
5219 if( mesg==WM_COMMAND )
5221 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5222 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5224 else if( mesg==WM_SYSCOMMAND )
5226 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5227 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5229 else
5231 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5232 * #0: unknown (please report!)
5233 * #1: for WM_KEYUP,WM_SYSKEYUP
5234 * #2: mouse is captured
5235 * #3: window is disabled
5236 * #4: it's a disabled system menu option
5237 * #5: it's a menu option, but window is iconic
5238 * #6: it's a menu option, but disabled
5240 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5241 if(mesg==0)
5242 ERR_(accel)(" unknown reason - please report!\n");
5244 return TRUE;
5247 /**********************************************************************
5248 * TranslateAcceleratorA (USER32.@)
5249 * TranslateAccelerator (USER32.@)
5251 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5253 /* YES, Accel16! */
5254 LPACCEL16 lpAccelTbl;
5255 int i;
5256 WPARAM wParam;
5258 if (!hWnd || !msg) return 0;
5260 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5262 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5263 return 0;
5266 wParam = msg->wParam;
5268 switch (msg->message)
5270 case WM_KEYDOWN:
5271 case WM_SYSKEYDOWN:
5272 break;
5274 case WM_CHAR:
5275 case WM_SYSCHAR:
5277 char ch = LOWORD(wParam);
5278 WCHAR wch;
5279 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5280 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5282 break;
5284 default:
5285 return 0;
5288 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5289 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5290 i = 0;
5293 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5294 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5295 return 1;
5296 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5298 return 0;
5301 /**********************************************************************
5302 * TranslateAcceleratorW (USER32.@)
5304 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5306 /* YES, Accel16! */
5307 LPACCEL16 lpAccelTbl;
5308 int i;
5310 if (!hWnd || !msg) return 0;
5312 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5314 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5315 return 0;
5318 switch (msg->message)
5320 case WM_KEYDOWN:
5321 case WM_SYSKEYDOWN:
5322 case WM_CHAR:
5323 case WM_SYSCHAR:
5324 break;
5326 default:
5327 return 0;
5330 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5331 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5332 i = 0;
5335 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5336 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5337 return 1;
5338 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5340 return 0;