winecfg: Fix the Bulgarian translation.
[wine.git] / dlls / user32 / menu.c
blobdd859b170ec0936de802780eaea079df11a07552
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 0x0001
120 #define TF_SUSPENDPOPUP 0x0002
121 #define TF_SKIPREMOVE 0x0004
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;
1507 } else {
1508 bmprc.left = 4;
1509 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1510 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1512 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1513 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1514 bmprc.top = 0;
1515 else
1516 bmprc.top = (rect.bottom - rect.top -
1517 lpitem->bmpsize.cy) / 2;
1518 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1521 if (!menuBar)
1523 HBITMAP bm;
1524 INT y = rect.top + rect.bottom;
1525 RECT rc = rect;
1526 int checked = FALSE;
1527 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1528 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1529 /* Draw the check mark
1531 * FIXME:
1532 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1534 if( !(menu->dwStyle & MNS_NOCHECK)) {
1535 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1536 lpitem->hUnCheckBit;
1537 if (bm) /* we have a custom bitmap */
1539 HDC hdcMem = CreateCompatibleDC( hdc );
1541 SelectObject( hdcMem, bm );
1542 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1543 check_bitmap_width, check_bitmap_height,
1544 hdcMem, 0, 0, SRCCOPY );
1545 DeleteDC( hdcMem );
1546 checked = TRUE;
1548 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1550 RECT r;
1551 HBITMAP bm = CreateBitmap( check_bitmap_width,
1552 check_bitmap_height, 1, 1, NULL );
1553 HDC hdcMem = CreateCompatibleDC( hdc );
1555 SelectObject( hdcMem, bm );
1556 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1557 DrawFrameControl( hdcMem, &r, DFC_MENU,
1558 (lpitem->fType & MFT_RADIOCHECK) ?
1559 DFCS_MENUBULLET : DFCS_MENUCHECK );
1560 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1561 hdcMem, 0, 0, SRCCOPY );
1562 DeleteDC( hdcMem );
1563 DeleteObject( bm );
1564 checked = TRUE;
1567 if( lpitem->hbmpItem &&
1568 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1569 POINT origorg;
1570 /* some applications make this assumption on the DC's origin */
1571 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1572 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1573 odaction, FALSE);
1574 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1576 /* Draw the popup-menu arrow */
1577 if (lpitem->fType & MF_POPUP)
1578 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1579 arrow_bitmap_height);
1580 rect.left += 4;
1581 if( !(menu->dwStyle & MNS_NOCHECK))
1582 rect.left += check_bitmap_width;
1583 rect.right -= arrow_bitmap_width;
1585 else if( lpitem->hbmpItem)
1586 { /* Draw the bitmap */
1587 POINT origorg;
1589 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1590 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1591 odaction, menuBar);
1592 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1594 /* process text if present */
1595 if (lpitem->text)
1597 register int i;
1598 HFONT hfontOld = 0;
1600 UINT uFormat = (menuBar) ?
1601 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1602 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1604 if( !(menu->dwStyle & MNS_CHECKORBMP))
1605 rect.left += menu->maxBmpSize.cx;
1607 if ( lpitem->fState & MFS_DEFAULT )
1609 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1612 if (menuBar) {
1613 if( lpitem->hbmpItem)
1614 rect.left += lpitem->bmpsize.cx;
1615 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1616 rect.left += menucharsize.cx;
1617 rect.right -= menucharsize.cx;
1620 for (i = 0; lpitem->text[i]; i++)
1621 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1622 break;
1624 if(lpitem->fState & MF_GRAYED)
1626 if (!(lpitem->fState & MF_HILITE) )
1628 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1629 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1630 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1631 --rect.left; --rect.top; --rect.right; --rect.bottom;
1633 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1636 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1638 /* paint the shortcut text */
1639 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1641 if (lpitem->text[i] == '\t')
1643 rect.left = lpitem->xTab;
1644 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1646 else
1648 rect.right = lpitem->xTab;
1649 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1652 if(lpitem->fState & MF_GRAYED)
1654 if (!(lpitem->fState & MF_HILITE) )
1656 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1657 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1658 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1659 --rect.left; --rect.top; --rect.right; --rect.bottom;
1661 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1663 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1666 if (hfontOld)
1667 SelectObject (hdc, hfontOld);
1672 /***********************************************************************
1673 * MENU_DrawPopupMenu
1675 * Paint a popup menu.
1677 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1679 HBRUSH hPrevBrush = 0;
1680 RECT rect;
1682 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1684 GetClientRect( hwnd, &rect );
1686 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1687 && (SelectObject( hdc, get_menu_font(FALSE))))
1689 HPEN hPrevPen;
1691 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1693 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1694 if( hPrevPen )
1696 POPUPMENU *menu;
1697 BOOL flat_menu = FALSE;
1699 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1700 if (flat_menu)
1701 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1702 else
1703 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1705 if( (menu = MENU_GetMenu( hmenu )))
1707 /* draw menu items */
1708 if( menu->nItems)
1710 MENUITEM *item;
1711 UINT u;
1713 item = menu->items;
1714 for( u = menu->nItems; u > 0; u--, item++)
1715 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1716 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1718 /* draw scroll arrows */
1719 if (menu->bScrolling)
1720 MENU_DrawScrollArrows(menu, hdc);
1722 } else
1724 SelectObject( hdc, hPrevBrush );
1729 /***********************************************************************
1730 * MENU_DrawMenuBar
1732 * Paint a menu bar. Returns the height of the menu bar.
1733 * called from [windows/nonclient.c]
1735 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1736 BOOL suppress_draw)
1738 LPPOPUPMENU lppop;
1739 HFONT hfontOld = 0;
1740 HMENU hMenu = GetMenu(hwnd);
1742 lppop = MENU_GetMenu( hMenu );
1743 if (lppop == NULL || lprect == NULL)
1745 return GetSystemMetrics(SM_CYMENU);
1748 if (suppress_draw)
1750 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1752 if (lppop->Height == 0)
1753 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1755 lprect->bottom = lprect->top + lppop->Height;
1757 if (hfontOld) SelectObject( hDC, hfontOld);
1758 return lppop->Height;
1760 else
1761 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1765 /***********************************************************************
1766 * MENU_ShowPopup
1768 * Display a popup menu.
1770 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1771 INT x, INT y, INT xanchor, INT yanchor )
1773 POPUPMENU *menu;
1774 INT width, height;
1775 POINT pt;
1776 HMONITOR monitor;
1777 MONITORINFO info;
1779 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1780 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1782 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1783 if (menu->FocusedItem != NO_SELECTED_ITEM)
1785 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1786 menu->FocusedItem = NO_SELECTED_ITEM;
1789 /* store the owner for DrawItem */
1790 menu->hwndOwner = hwndOwner;
1792 menu->nScrollPos = 0;
1793 MENU_PopupMenuCalcSize( menu );
1795 /* adjust popup menu pos so that it fits within the desktop */
1797 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1798 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1800 /* FIXME: should use item rect */
1801 pt.x = x;
1802 pt.y = y;
1803 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1804 info.cbSize = sizeof(info);
1805 GetMonitorInfoW( monitor, &info );
1806 if( x + width > info.rcWork.right)
1808 if( xanchor && x >= width - xanchor )
1809 x -= width - xanchor;
1811 if( x + width > info.rcWork.right)
1812 x = info.rcWork.right - width;
1814 if( x < info.rcWork.left ) x = info.rcWork.left;
1816 if( y + height > info.rcWork.bottom)
1818 if( yanchor && y >= height + yanchor )
1819 y -= height + yanchor;
1821 if( y + height > info.rcWork.bottom)
1822 y = info.rcWork.bottom - height;
1824 if( y < info.rcWork.top ) y = info.rcWork.top;
1826 /* NOTE: In Windows, top menu popup is not owned. */
1827 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1828 WS_POPUP, x, y, width, height,
1829 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1830 (LPVOID)hmenu );
1831 if( !menu->hWnd ) return FALSE;
1832 if (!top_popup) top_popup = menu->hWnd;
1834 /* Display the window */
1836 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1837 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1838 UpdateWindow( menu->hWnd );
1839 return TRUE;
1843 /***********************************************************************
1844 * MENU_EnsureMenuItemVisible
1846 static void
1847 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1849 if (lppop->bScrolling)
1851 MENUITEM *item = &lppop->items[wIndex];
1852 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1853 UINT nOldPos = lppop->nScrollPos;
1854 RECT rc;
1855 UINT arrow_bitmap_height;
1856 BITMAP bmp;
1858 GetClientRect(lppop->hWnd, &rc);
1860 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1861 arrow_bitmap_height = bmp.bmHeight;
1863 rc.top += arrow_bitmap_height;
1864 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1866 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1867 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1870 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1871 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1872 MENU_DrawScrollArrows(lppop, hdc);
1874 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1876 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1877 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1878 MENU_DrawScrollArrows(lppop, hdc);
1884 /***********************************************************************
1885 * MENU_SelectItem
1887 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1888 BOOL sendMenuSelect, HMENU topmenu )
1890 LPPOPUPMENU lppop;
1891 HDC hdc;
1893 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1895 lppop = MENU_GetMenu( hmenu );
1896 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1898 if (lppop->FocusedItem == wIndex) return;
1899 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1900 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1901 if (!top_popup) top_popup = lppop->hWnd;
1903 SelectObject( hdc, get_menu_font(FALSE));
1905 /* Clear previous highlighted item */
1906 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1908 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1909 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1910 lppop->Height, !(lppop->wFlags & MF_POPUP),
1911 ODA_SELECT );
1914 /* Highlight new item (if any) */
1915 lppop->FocusedItem = wIndex;
1916 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1918 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1919 lppop->items[wIndex].fState |= MF_HILITE;
1920 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1921 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1922 &lppop->items[wIndex], lppop->Height,
1923 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1925 if (sendMenuSelect)
1927 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1928 SendMessageW( hwndOwner, WM_MENUSELECT,
1929 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1930 ip->fType | ip->fState |
1931 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1934 else if (sendMenuSelect) {
1935 if(topmenu){
1936 int pos;
1937 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1938 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1939 MENUITEM *ip = &ptm->items[pos];
1940 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1941 ip->fType | ip->fState |
1942 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1946 ReleaseDC( lppop->hWnd, hdc );
1950 /***********************************************************************
1951 * MENU_MoveSelection
1953 * Moves currently selected item according to the offset parameter.
1954 * If there is no selection then it should select the last item if
1955 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1957 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1959 INT i;
1960 POPUPMENU *menu;
1962 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1964 menu = MENU_GetMenu( hmenu );
1965 if ((!menu) || (!menu->items)) return;
1967 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1969 if( menu->nItems == 1 ) return; else
1970 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1971 ; i += offset)
1972 if (!(menu->items[i].fType & MF_SEPARATOR))
1974 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1975 return;
1979 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1980 i >= 0 && i < menu->nItems ; i += offset)
1981 if (!(menu->items[i].fType & MF_SEPARATOR))
1983 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1984 return;
1989 /**********************************************************************
1990 * MENU_SetItemData
1992 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1993 * ModifyMenu().
1995 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1996 LPCWSTR str )
1998 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1999 TRACE("flags=%x str=%p\n", flags, str);
2001 if (IS_STRING_ITEM(flags))
2003 LPWSTR prevText = item->text;
2004 if (!str)
2006 flags |= MF_SEPARATOR;
2007 item->text = NULL;
2009 else
2011 LPWSTR text;
2012 /* Item beginning with a backspace is a help item */
2013 if (*str == '\b')
2015 flags |= MF_HELP;
2016 str++;
2018 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2019 return FALSE;
2020 strcpyW( text, str );
2021 item->text = text;
2023 item->hbmpItem = NULL;
2024 HeapFree( GetProcessHeap(), 0, prevText );
2026 else if(( flags & MFT_BITMAP)) {
2027 item->hbmpItem = HBITMAP_32(LOWORD(str));
2028 /* setting bitmap clears text */
2029 HeapFree( GetProcessHeap(), 0, item->text );
2030 item->text = NULL;
2033 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2035 if (flags & MF_OWNERDRAW)
2036 item->dwItemData = (DWORD_PTR)str;
2037 else
2038 item->dwItemData = 0;
2040 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2041 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2043 if (flags & MF_POPUP)
2045 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2046 if (menu) menu->wFlags |= MF_POPUP;
2047 else
2049 item->wID = 0;
2050 item->hSubMenu = 0;
2051 item->fType = 0;
2052 item->fState = 0;
2053 return FALSE;
2057 item->wID = id;
2058 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2060 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2061 flags |= MF_POPUP; /* keep popup */
2063 item->fType = flags & TYPE_MASK;
2064 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2065 for ModifyMenu, but Windows accepts it */
2066 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2068 /* Don't call SetRectEmpty here! */
2070 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2071 return TRUE;
2075 /**********************************************************************
2076 * MENU_InsertItem
2078 * Insert (allocate) a new item into a menu.
2080 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2082 MENUITEM *newItems;
2083 POPUPMENU *menu;
2085 if (!(menu = MENU_GetMenu(hMenu)))
2086 return NULL;
2088 /* Find where to insert new item */
2090 if (flags & MF_BYPOSITION) {
2091 if (pos > menu->nItems)
2092 pos = menu->nItems;
2093 } else {
2094 if (!MENU_FindItem( &hMenu, &pos, flags ))
2095 pos = menu->nItems;
2096 else {
2097 if (!(menu = MENU_GetMenu( hMenu )))
2098 return NULL;
2102 /* Make sure that MDI system buttons stay on the right side.
2103 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2104 * regardless of their id.
2106 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2107 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2108 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2109 pos--;
2111 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2113 /* Create new items array */
2115 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2116 if (!newItems)
2118 WARN("allocation failed\n" );
2119 return NULL;
2121 if (menu->nItems > 0)
2123 /* Copy the old array into the new one */
2124 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2125 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2126 (menu->nItems-pos)*sizeof(MENUITEM) );
2127 HeapFree( GetProcessHeap(), 0, menu->items );
2129 menu->items = newItems;
2130 menu->nItems++;
2131 memset( &newItems[pos], 0, sizeof(*newItems) );
2132 menu->Height = 0; /* force size recalculate */
2133 return &newItems[pos];
2137 /**********************************************************************
2138 * MENU_ParseResource
2140 * Parse a standard menu resource and add items to the menu.
2141 * Return a pointer to the end of the resource.
2143 * NOTE: flags is equivalent to the mtOption field
2145 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2147 WORD flags, id = 0;
2148 LPCSTR str;
2149 BOOL end_flag;
2153 flags = GET_WORD(res);
2154 end_flag = flags & MF_END;
2155 /* Remove MF_END because it has the same value as MF_HILITE */
2156 flags &= ~MF_END;
2157 res += sizeof(WORD);
2158 if (!(flags & MF_POPUP))
2160 id = GET_WORD(res);
2161 res += sizeof(WORD);
2163 str = res;
2164 if (!unicode) res += strlen(str) + 1;
2165 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2166 if (flags & MF_POPUP)
2168 HMENU hSubMenu = CreatePopupMenu();
2169 if (!hSubMenu) return NULL;
2170 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2171 return NULL;
2172 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2173 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2175 else /* Not a popup */
2177 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2178 else AppendMenuW( hMenu, flags, id,
2179 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2181 } while (!end_flag);
2182 return res;
2186 /**********************************************************************
2187 * MENUEX_ParseResource
2189 * Parse an extended menu resource and add items to the menu.
2190 * Return a pointer to the end of the resource.
2192 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2194 WORD resinfo;
2195 do {
2196 MENUITEMINFOW mii;
2198 mii.cbSize = sizeof(mii);
2199 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2200 mii.fType = GET_DWORD(res);
2201 res += sizeof(DWORD);
2202 mii.fState = GET_DWORD(res);
2203 res += sizeof(DWORD);
2204 mii.wID = GET_DWORD(res);
2205 res += sizeof(DWORD);
2206 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2207 res += sizeof(WORD);
2208 /* Align the text on a word boundary. */
2209 res += (~((UINT_PTR)res - 1)) & 1;
2210 mii.dwTypeData = (LPWSTR) res;
2211 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2212 /* Align the following fields on a dword boundary. */
2213 res += (~((UINT_PTR)res - 1)) & 3;
2215 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2216 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2218 if (resinfo & 1) { /* Pop-up? */
2219 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2220 res += sizeof(DWORD);
2221 mii.hSubMenu = CreatePopupMenu();
2222 if (!mii.hSubMenu)
2223 return NULL;
2224 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2225 DestroyMenu(mii.hSubMenu);
2226 return NULL;
2228 mii.fMask |= MIIM_SUBMENU;
2229 mii.fType |= MF_POPUP;
2231 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2233 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2234 mii.wID, mii.fType);
2235 mii.fType |= MF_SEPARATOR;
2237 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2238 } while (!(resinfo & MF_END));
2239 return res;
2243 /***********************************************************************
2244 * MENU_GetSubPopup
2246 * Return the handle of the selected sub-popup menu (if any).
2248 static HMENU MENU_GetSubPopup( HMENU hmenu )
2250 POPUPMENU *menu;
2251 MENUITEM *item;
2253 menu = MENU_GetMenu( hmenu );
2255 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2257 item = &menu->items[menu->FocusedItem];
2258 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2259 return item->hSubMenu;
2260 return 0;
2264 /***********************************************************************
2265 * MENU_HideSubPopups
2267 * Hide the sub-popup menus of this menu.
2269 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2270 BOOL sendMenuSelect )
2272 POPUPMENU *menu = MENU_GetMenu( hmenu );
2274 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2276 if (menu && top_popup)
2278 HMENU hsubmenu;
2279 POPUPMENU *submenu;
2280 MENUITEM *item;
2282 if (menu->FocusedItem != NO_SELECTED_ITEM)
2284 item = &menu->items[menu->FocusedItem];
2285 if (!(item->fType & MF_POPUP) ||
2286 !(item->fState & MF_MOUSESELECT)) return;
2287 item->fState &= ~MF_MOUSESELECT;
2288 hsubmenu = item->hSubMenu;
2289 } else return;
2291 submenu = MENU_GetMenu( hsubmenu );
2292 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2293 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2294 DestroyWindow( submenu->hWnd );
2295 submenu->hWnd = 0;
2300 /***********************************************************************
2301 * MENU_ShowSubPopup
2303 * Display the sub-menu of the selected item of this menu.
2304 * Return the handle of the submenu, or hmenu if no submenu to display.
2306 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2307 BOOL selectFirst, UINT wFlags )
2309 RECT rect;
2310 POPUPMENU *menu;
2311 MENUITEM *item;
2312 HDC hdc;
2314 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2316 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2318 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2320 item = &menu->items[menu->FocusedItem];
2321 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2322 return hmenu;
2324 /* message must be sent before using item,
2325 because nearly everything may be changed by the application ! */
2327 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2328 if (!(wFlags & TPM_NONOTIFY))
2329 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2330 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2332 item = &menu->items[menu->FocusedItem];
2333 rect = item->rect;
2335 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2336 if (!(item->fState & MF_HILITE))
2338 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2339 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2341 SelectObject( hdc, get_menu_font(FALSE));
2343 item->fState |= MF_HILITE;
2344 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2345 ReleaseDC( menu->hWnd, hdc );
2347 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2348 item->rect = rect;
2350 item->fState |= MF_MOUSESELECT;
2352 if (IS_SYSTEM_MENU(menu))
2354 MENU_InitSysMenuPopup(item->hSubMenu,
2355 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2356 GetClassLongW( menu->hWnd, GCL_STYLE));
2358 NC_GetSysPopupPos( menu->hWnd, &rect );
2359 rect.top = rect.bottom;
2360 rect.right = GetSystemMetrics(SM_CXSIZE);
2361 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2363 else
2365 GetWindowRect( menu->hWnd, &rect );
2366 if (menu->wFlags & MF_POPUP)
2368 RECT rc = item->rect;
2370 MENU_AdjustMenuItemRect(menu, &rc);
2372 /* The first item in the popup menu has to be at the
2373 same y position as the focused menu item */
2374 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2375 rect.top += rc.top - MENU_TOP_MARGIN;
2376 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2377 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2378 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2380 else
2382 rect.left += item->rect.left;
2383 rect.top += item->rect.bottom;
2384 rect.right = item->rect.right - item->rect.left;
2385 rect.bottom = item->rect.bottom - item->rect.top;
2389 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2390 rect.left, rect.top, rect.right, rect.bottom );
2391 if (selectFirst)
2392 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2393 return item->hSubMenu;
2398 /**********************************************************************
2399 * MENU_IsMenuActive
2401 HWND MENU_IsMenuActive(void)
2403 return top_popup;
2406 /***********************************************************************
2407 * MENU_PtMenu
2409 * Walks menu chain trying to find a menu pt maps to.
2411 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2413 POPUPMENU *menu = MENU_GetMenu( hMenu );
2414 UINT item = menu->FocusedItem;
2415 HMENU ret;
2417 /* try subpopup first (if any) */
2418 ret = (item != NO_SELECTED_ITEM &&
2419 (menu->items[item].fType & MF_POPUP) &&
2420 (menu->items[item].fState & MF_MOUSESELECT))
2421 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2423 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2425 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2426 if( menu->wFlags & MF_POPUP )
2428 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2430 else if (ht == HTSYSMENU)
2431 ret = get_win_sys_menu( menu->hWnd );
2432 else if (ht == HTMENU)
2433 ret = GetMenu( menu->hWnd );
2435 return ret;
2438 /***********************************************************************
2439 * MENU_ExecFocusedItem
2441 * Execute a menu item (for instance when user pressed Enter).
2442 * Return the wID of the executed item. Otherwise, -1 indicating
2443 * that no menu item was executed, -2 if a popup is shown;
2444 * Have to receive the flags for the TrackPopupMenu options to avoid
2445 * sending unwanted message.
2448 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2450 MENUITEM *item;
2451 POPUPMENU *menu = MENU_GetMenu( hMenu );
2453 TRACE("%p hmenu=%p\n", pmt, hMenu);
2455 if (!menu || !menu->nItems ||
2456 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2458 item = &menu->items[menu->FocusedItem];
2460 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2462 if (!(item->fType & MF_POPUP))
2464 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2466 /* If TPM_RETURNCMD is set you return the id, but
2467 do not send a message to the owner */
2468 if(!(wFlags & TPM_RETURNCMD))
2470 if( menu->wFlags & MF_SYSMENU )
2471 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2472 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2473 else
2475 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2476 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2478 if (dwStyle & MNS_NOTIFYBYPOS)
2479 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2480 (LPARAM)hMenu);
2481 else
2482 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2485 return item->wID;
2488 else
2490 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2491 return -2;
2494 return -1;
2497 /***********************************************************************
2498 * MENU_SwitchTracking
2500 * Helper function for menu navigation routines.
2502 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2504 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2505 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2507 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2509 if( pmt->hTopMenu != hPtMenu &&
2510 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2512 /* both are top level menus (system and menu-bar) */
2513 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2514 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2515 pmt->hTopMenu = hPtMenu;
2517 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2518 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2522 /***********************************************************************
2523 * MENU_ButtonDown
2525 * Return TRUE if we can go on with menu tracking.
2527 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2529 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2531 if (hPtMenu)
2533 UINT id = 0;
2534 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2535 MENUITEM *item;
2537 if( IS_SYSTEM_MENU(ptmenu) )
2538 item = ptmenu->items;
2539 else
2540 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2542 if( item )
2544 if( ptmenu->FocusedItem != id )
2545 MENU_SwitchTracking( pmt, hPtMenu, id );
2547 /* If the popup menu is not already "popped" */
2548 if(!(item->fState & MF_MOUSESELECT ))
2550 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2553 return TRUE;
2555 /* Else the click was on the menu bar, finish the tracking */
2557 return FALSE;
2560 /***********************************************************************
2561 * MENU_ButtonUp
2563 * Return the value of MENU_ExecFocusedItem if
2564 * the selected item was not a popup. Else open the popup.
2565 * A -1 return value indicates that we go on with menu tracking.
2568 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2570 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2572 if (hPtMenu)
2574 UINT id = 0;
2575 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2576 MENUITEM *item;
2578 if( IS_SYSTEM_MENU(ptmenu) )
2579 item = ptmenu->items;
2580 else
2581 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2583 if( item && (ptmenu->FocusedItem == id ))
2585 debug_print_menuitem ("FocusedItem: ", item, "");
2587 if( !(item->fType & MF_POPUP) )
2589 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2590 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2591 return executedMenuId;
2594 /* If we are dealing with the top-level menu */
2595 /* and this is a click on an already "popped" item: */
2596 /* Stop the menu tracking and close the opened submenus */
2597 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2598 return 0;
2600 ptmenu->bTimeToHide = TRUE;
2602 return -1;
2606 /***********************************************************************
2607 * MENU_MouseMove
2609 * Return TRUE if we can go on with menu tracking.
2611 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2613 UINT id = NO_SELECTED_ITEM;
2614 POPUPMENU *ptmenu = NULL;
2616 if( hPtMenu )
2618 ptmenu = MENU_GetMenu( hPtMenu );
2619 if( IS_SYSTEM_MENU(ptmenu) )
2620 id = 0;
2621 else
2622 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2625 if( id == NO_SELECTED_ITEM )
2627 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2628 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2631 else if( ptmenu->FocusedItem != id )
2633 MENU_SwitchTracking( pmt, hPtMenu, id );
2634 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2636 return TRUE;
2640 /***********************************************************************
2641 * MENU_DoNextMenu
2643 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2645 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2647 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2648 BOOL atEnd = FALSE;
2650 /* When skipping left, we need to do something special after the
2651 first menu. */
2652 if (vk == VK_LEFT && menu->FocusedItem == 0)
2654 atEnd = TRUE;
2656 /* When skipping right, for the non-system menu, we need to
2657 handle the last non-special menu item (ie skip any window
2658 icons such as MDI maximize, restore or close) */
2659 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2661 int i = menu->FocusedItem + 1;
2662 while (i < menu->nItems) {
2663 if ((menu->items[i].wID >= SC_SIZE &&
2664 menu->items[i].wID <= SC_RESTORE)) {
2665 i++;
2666 } else break;
2668 if (i == menu->nItems) {
2669 atEnd = TRUE;
2672 /* When skipping right, we need to cater for the system menu */
2673 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2675 if (menu->FocusedItem == (menu->nItems - 1)) {
2676 atEnd = TRUE;
2680 if( atEnd )
2682 MDINEXTMENU next_menu;
2683 HMENU hNewMenu;
2684 HWND hNewWnd;
2685 UINT id = 0;
2687 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2688 next_menu.hmenuNext = 0;
2689 next_menu.hwndNext = 0;
2690 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2692 TRACE("%p [%p] -> %p [%p]\n",
2693 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2695 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2697 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2698 hNewWnd = pmt->hOwnerWnd;
2699 if( IS_SYSTEM_MENU(menu) )
2701 /* switch to the menu bar */
2703 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2705 if( vk == VK_LEFT )
2707 menu = MENU_GetMenu( hNewMenu );
2708 id = menu->nItems - 1;
2710 /* Skip backwards over any system predefined icons,
2711 eg. MDI close, restore etc icons */
2712 while ((id > 0) &&
2713 (menu->items[id].wID >= SC_SIZE &&
2714 menu->items[id].wID <= SC_RESTORE)) id--;
2717 else if (style & WS_SYSMENU )
2719 /* switch to the system menu */
2720 hNewMenu = get_win_sys_menu( hNewWnd );
2722 else return FALSE;
2724 else /* application returned a new menu to switch to */
2726 hNewMenu = next_menu.hmenuNext;
2727 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2729 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2731 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2733 if (style & WS_SYSMENU &&
2734 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2736 /* get the real system menu */
2737 hNewMenu = get_win_sys_menu(hNewWnd);
2739 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2741 /* FIXME: Not sure what to do here;
2742 * perhaps try to track hNewMenu as a popup? */
2744 TRACE(" -- got confused.\n");
2745 return FALSE;
2748 else return FALSE;
2751 if( hNewMenu != pmt->hTopMenu )
2753 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2754 FALSE, 0 );
2755 if( pmt->hCurrentMenu != pmt->hTopMenu )
2756 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2759 if( hNewWnd != pmt->hOwnerWnd )
2761 pmt->hOwnerWnd = hNewWnd;
2762 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2765 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2766 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2768 return TRUE;
2770 return FALSE;
2773 /***********************************************************************
2774 * MENU_SuspendPopup
2776 * The idea is not to show the popup if the next input message is
2777 * going to hide it anyway.
2779 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2781 MSG msg;
2783 msg.hwnd = pmt->hOwnerWnd;
2785 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2786 pmt->trackFlags |= TF_SKIPREMOVE;
2788 switch( uMsg )
2790 case WM_KEYDOWN:
2791 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2792 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2794 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2795 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2796 if( msg.message == WM_KEYDOWN &&
2797 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2799 pmt->trackFlags |= TF_SUSPENDPOPUP;
2800 return TRUE;
2803 break;
2806 /* failures go through this */
2807 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2808 return FALSE;
2811 /***********************************************************************
2812 * MENU_KeyEscape
2814 * Handle a VK_ESCAPE key event in a menu.
2816 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2818 BOOL bEndMenu = TRUE;
2820 if (pmt->hCurrentMenu != pmt->hTopMenu)
2822 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2824 if (menu->wFlags & MF_POPUP)
2826 HMENU hmenutmp, hmenuprev;
2828 hmenuprev = hmenutmp = pmt->hTopMenu;
2830 /* close topmost popup */
2831 while (hmenutmp != pmt->hCurrentMenu)
2833 hmenuprev = hmenutmp;
2834 hmenutmp = MENU_GetSubPopup( hmenuprev );
2837 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2838 pmt->hCurrentMenu = hmenuprev;
2839 bEndMenu = FALSE;
2843 return bEndMenu;
2846 /***********************************************************************
2847 * MENU_KeyLeft
2849 * Handle a VK_LEFT key event in a menu.
2851 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2853 POPUPMENU *menu;
2854 HMENU hmenutmp, hmenuprev;
2855 UINT prevcol;
2857 hmenuprev = hmenutmp = pmt->hTopMenu;
2858 menu = MENU_GetMenu( hmenutmp );
2860 /* Try to move 1 column left (if possible) */
2861 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2862 NO_SELECTED_ITEM ) {
2864 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2865 prevcol, TRUE, 0 );
2866 return;
2869 /* close topmost popup */
2870 while (hmenutmp != pmt->hCurrentMenu)
2872 hmenuprev = hmenutmp;
2873 hmenutmp = MENU_GetSubPopup( hmenuprev );
2876 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2877 pmt->hCurrentMenu = hmenuprev;
2879 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2881 /* move menu bar selection if no more popups are left */
2883 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2884 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2886 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2888 /* A sublevel menu was displayed - display the next one
2889 * unless there is another displacement coming up */
2891 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2892 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2893 pmt->hTopMenu, TRUE, wFlags);
2899 /***********************************************************************
2900 * MENU_KeyRight
2902 * Handle a VK_RIGHT key event in a menu.
2904 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2906 HMENU hmenutmp;
2907 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2908 UINT nextcol;
2910 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2911 pmt->hCurrentMenu,
2912 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2913 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2915 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2917 /* If already displaying a popup, try to display sub-popup */
2919 hmenutmp = pmt->hCurrentMenu;
2920 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2922 /* if subpopup was displayed then we are done */
2923 if (hmenutmp != pmt->hCurrentMenu) return;
2926 /* Check to see if there's another column */
2927 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2928 NO_SELECTED_ITEM ) {
2929 TRACE("Going to %d.\n", nextcol );
2930 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2931 nextcol, TRUE, 0 );
2932 return;
2935 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2937 if( pmt->hCurrentMenu != pmt->hTopMenu )
2939 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2940 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2941 } else hmenutmp = 0;
2943 /* try to move to the next item */
2944 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2945 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2947 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2948 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2949 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2950 pmt->hTopMenu, TRUE, wFlags);
2954 /***********************************************************************
2955 * MENU_TrackMenu
2957 * Menu tracking code.
2959 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2960 HWND hwnd, const RECT *lprect )
2962 MSG msg;
2963 POPUPMENU *menu;
2964 BOOL fRemove;
2965 INT executedMenuId = -1;
2966 MTRACKER mt;
2967 BOOL enterIdleSent = FALSE;
2968 HWND capture_win;
2970 mt.trackFlags = 0;
2971 mt.hCurrentMenu = hmenu;
2972 mt.hTopMenu = hmenu;
2973 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2974 mt.pt.x = x;
2975 mt.pt.y = y;
2977 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2978 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2980 fEndMenu = FALSE;
2981 if (!(menu = MENU_GetMenu( hmenu )))
2983 WARN("Invalid menu handle %p\n", hmenu);
2984 SetLastError(ERROR_INVALID_MENU_HANDLE);
2985 return FALSE;
2988 if (wFlags & TPM_BUTTONDOWN)
2990 /* Get the result in order to start the tracking or not */
2991 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2992 fEndMenu = !fRemove;
2995 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2997 /* owner may not be visible when tracking a popup, so use the menu itself */
2998 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
2999 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3001 while (!fEndMenu)
3003 menu = MENU_GetMenu( mt.hCurrentMenu );
3004 if (!menu) /* sometimes happens if I do a window manager close */
3005 break;
3007 /* we have to keep the message in the queue until it's
3008 * clear that menu loop is not over yet. */
3010 for (;;)
3012 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3014 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3015 /* remove the message from the queue */
3016 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3018 else
3020 if (!enterIdleSent)
3022 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3023 enterIdleSent = TRUE;
3024 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3026 WaitMessage();
3030 /* check if EndMenu() tried to cancel us, by posting this message */
3031 if(msg.message == WM_CANCELMODE)
3033 /* we are now out of the loop */
3034 fEndMenu = TRUE;
3036 /* remove the message from the queue */
3037 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3039 /* break out of internal loop, ala ESCAPE */
3040 break;
3043 TranslateMessage( &msg );
3044 mt.pt = msg.pt;
3046 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3047 enterIdleSent=FALSE;
3049 fRemove = FALSE;
3050 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3053 * Use the mouse coordinates in lParam instead of those in the MSG
3054 * struct to properly handle synthetic messages. They are already
3055 * in screen coordinates.
3057 mt.pt.x = (short)LOWORD(msg.lParam);
3058 mt.pt.y = (short)HIWORD(msg.lParam);
3060 /* Find a menu for this mouse event */
3061 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3063 switch(msg.message)
3065 /* no WM_NC... messages in captured state */
3067 case WM_RBUTTONDBLCLK:
3068 case WM_RBUTTONDOWN:
3069 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3070 /* fall through */
3071 case WM_LBUTTONDBLCLK:
3072 case WM_LBUTTONDOWN:
3073 /* If the message belongs to the menu, removes it from the queue */
3074 /* Else, end menu tracking */
3075 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3076 fEndMenu = !fRemove;
3077 break;
3079 case WM_RBUTTONUP:
3080 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3081 /* fall through */
3082 case WM_LBUTTONUP:
3083 /* Check if a menu was selected by the mouse */
3084 if (hmenu)
3086 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3087 TRACE("executedMenuId %d\n", executedMenuId);
3089 /* End the loop if executedMenuId is an item ID */
3090 /* or if the job was done (executedMenuId = 0). */
3091 fEndMenu = fRemove = (executedMenuId != -1);
3093 /* No menu was selected by the mouse */
3094 /* if the function was called by TrackPopupMenu, continue
3095 with the menu tracking. If not, stop it */
3096 else
3097 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3099 break;
3101 case WM_MOUSEMOVE:
3102 /* the selected menu item must be changed every time */
3103 /* the mouse moves. */
3105 if (hmenu)
3106 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3108 } /* switch(msg.message) - mouse */
3110 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3112 fRemove = TRUE; /* Keyboard messages are always removed */
3113 switch(msg.message)
3115 case WM_KEYDOWN:
3116 case WM_SYSKEYDOWN:
3117 switch(msg.wParam)
3119 case VK_MENU:
3120 fEndMenu = TRUE;
3121 break;
3123 case VK_HOME:
3124 case VK_END:
3125 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3126 NO_SELECTED_ITEM, FALSE, 0 );
3127 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3128 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3129 break;
3131 case VK_UP:
3132 case VK_DOWN: /* If on menu bar, pull-down the menu */
3134 menu = MENU_GetMenu( mt.hCurrentMenu );
3135 if (!(menu->wFlags & MF_POPUP))
3136 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3137 else /* otherwise try to move selection */
3138 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3139 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3140 break;
3142 case VK_LEFT:
3143 MENU_KeyLeft( &mt, wFlags );
3144 break;
3146 case VK_RIGHT:
3147 MENU_KeyRight( &mt, wFlags );
3148 break;
3150 case VK_ESCAPE:
3151 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3152 break;
3154 case VK_F1:
3156 HELPINFO hi;
3157 hi.cbSize = sizeof(HELPINFO);
3158 hi.iContextType = HELPINFO_MENUITEM;
3159 if (menu->FocusedItem == NO_SELECTED_ITEM)
3160 hi.iCtrlId = 0;
3161 else
3162 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3163 hi.hItemHandle = hmenu;
3164 hi.dwContextId = menu->dwContextHelpID;
3165 hi.MousePos = msg.pt;
3166 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3167 break;
3170 default:
3171 break;
3173 break; /* WM_KEYDOWN */
3175 case WM_CHAR:
3176 case WM_SYSCHAR:
3178 UINT pos;
3180 if (msg.wParam == '\r' || msg.wParam == ' ')
3182 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3183 fEndMenu = (executedMenuId != -2);
3185 break;
3188 /* Hack to avoid control chars. */
3189 /* We will find a better way real soon... */
3190 if (msg.wParam < 32) break;
3192 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3193 LOWORD(msg.wParam), FALSE );
3194 if (pos == (UINT)-2) fEndMenu = TRUE;
3195 else if (pos == (UINT)-1) MessageBeep(0);
3196 else
3198 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3199 TRUE, 0 );
3200 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3201 fEndMenu = (executedMenuId != -2);
3204 break;
3205 } /* switch(msg.message) - kbd */
3207 else
3209 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3210 DispatchMessageW( &msg );
3211 continue;
3214 if (!fEndMenu) fRemove = TRUE;
3216 /* finally remove message from the queue */
3218 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3219 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3220 else mt.trackFlags &= ~TF_SKIPREMOVE;
3223 set_capture_window( 0, GUI_INMENUMODE, NULL );
3225 /* If dropdown is still painted and the close box is clicked on
3226 then the menu will be destroyed as part of the DispatchMessage above.
3227 This will then invalidate the menu handle in mt.hTopMenu. We should
3228 check for this first. */
3229 if( IsMenu( mt.hTopMenu ) )
3231 menu = MENU_GetMenu( mt.hTopMenu );
3233 if( IsWindow( mt.hOwnerWnd ) )
3235 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3237 if (menu && (menu->wFlags & MF_POPUP))
3239 DestroyWindow( menu->hWnd );
3240 menu->hWnd = 0;
3242 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3243 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3246 /* Reset the variable for hiding menu */
3247 if( menu ) menu->bTimeToHide = FALSE;
3250 /* The return value is only used by TrackPopupMenu */
3251 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3252 if (executedMenuId == -1) executedMenuId = 0;
3253 return executedMenuId;
3256 /***********************************************************************
3257 * MENU_InitTracking
3259 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3261 POPUPMENU *menu;
3263 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3265 HideCaret(0);
3267 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3268 if (!(wFlags & TPM_NONOTIFY))
3269 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3271 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3273 if (!(wFlags & TPM_NONOTIFY))
3275 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3276 /* If an app changed/recreated menu bar entries in WM_INITMENU
3277 * menu sizes will be recalculated once the menu created/shown.
3281 /* This makes the menus of applications built with Delphi work.
3282 * It also enables menus to be displayed in more than one window,
3283 * but there are some bugs left that need to be fixed in this case.
3285 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3287 return TRUE;
3289 /***********************************************************************
3290 * MENU_ExitTracking
3292 static BOOL MENU_ExitTracking(HWND hWnd)
3294 TRACE("hwnd=%p\n", hWnd);
3296 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3297 ShowCaret(0);
3298 top_popup = 0;
3299 return TRUE;
3302 /***********************************************************************
3303 * MENU_TrackMouseMenuBar
3305 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3307 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3309 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3310 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3312 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3314 if (IsMenu(hMenu))
3316 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3317 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3318 MENU_ExitTracking(hWnd);
3323 /***********************************************************************
3324 * MENU_TrackKbdMenuBar
3326 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3328 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3330 UINT uItem = NO_SELECTED_ITEM;
3331 HMENU hTrackMenu;
3332 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3334 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3336 /* find window that has a menu */
3338 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3339 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3341 /* check if we have to track a system menu */
3343 hTrackMenu = GetMenu( hwnd );
3344 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3346 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3347 hTrackMenu = get_win_sys_menu( hwnd );
3348 uItem = 0;
3349 wParam |= HTSYSMENU; /* prevent item lookup */
3352 if (!IsMenu( hTrackMenu )) return;
3354 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3356 if( wChar && wChar != ' ' )
3358 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3359 if ( uItem >= (UINT)(-2) )
3361 if( uItem == (UINT)(-1) ) MessageBeep(0);
3362 /* schedule end of menu tracking */
3363 wFlags |= TF_ENDMENU;
3364 goto track_menu;
3368 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3370 if (!(wParam & HTSYSMENU) || wChar == ' ')
3372 if( uItem == NO_SELECTED_ITEM )
3373 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3374 else
3375 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3378 track_menu:
3379 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3380 MENU_ExitTracking( hwnd );
3384 /**********************************************************************
3385 * TrackPopupMenu (USER32.@)
3387 * Like the win32 API, the function return the command ID only if the
3388 * flag TPM_RETURNCMD is on.
3391 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3392 INT nReserved, HWND hWnd, const RECT *lpRect )
3394 BOOL ret = FALSE;
3396 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3397 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3399 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3401 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3402 if (!(wFlags & TPM_NONOTIFY))
3403 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3405 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3406 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3407 MENU_ExitTracking(hWnd);
3409 return ret;
3412 /**********************************************************************
3413 * TrackPopupMenuEx (USER32.@)
3415 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3416 HWND hWnd, LPTPMPARAMS lpTpm )
3418 FIXME("not fully implemented\n" );
3419 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3420 lpTpm ? &lpTpm->rcExclude : NULL );
3423 /***********************************************************************
3424 * PopupMenuWndProc
3426 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3428 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3430 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3432 switch(message)
3434 case WM_CREATE:
3436 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3437 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3438 return 0;
3441 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3442 return MA_NOACTIVATE;
3444 case WM_PAINT:
3446 PAINTSTRUCT ps;
3447 BeginPaint( hwnd, &ps );
3448 MENU_DrawPopupMenu( hwnd, ps.hdc,
3449 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3450 EndPaint( hwnd, &ps );
3451 return 0;
3453 case WM_ERASEBKGND:
3454 return 1;
3456 case WM_DESTROY:
3457 /* zero out global pointer in case resident popup window was destroyed. */
3458 if (hwnd == top_popup) top_popup = 0;
3459 break;
3461 case WM_SHOWWINDOW:
3463 if( wParam )
3465 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3467 else
3468 SetWindowLongPtrW( hwnd, 0, 0 );
3469 break;
3471 case MM_SETMENUHANDLE:
3472 SetWindowLongPtrW( hwnd, 0, wParam );
3473 break;
3475 case MM_GETMENUHANDLE:
3476 return GetWindowLongPtrW( hwnd, 0 );
3478 default:
3479 return DefWindowProcW( hwnd, message, wParam, lParam );
3481 return 0;
3485 /***********************************************************************
3486 * MENU_GetMenuBarHeight
3488 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3490 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3491 INT orgX, INT orgY )
3493 HDC hdc;
3494 RECT rectBar;
3495 LPPOPUPMENU lppop;
3497 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3499 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3501 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3502 SelectObject( hdc, get_menu_font(FALSE));
3503 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3504 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3505 ReleaseDC( hwnd, hdc );
3506 return lppop->Height;
3510 /*******************************************************************
3511 * ChangeMenuA (USER32.@)
3513 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3514 UINT id, UINT flags )
3516 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3517 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3518 id, data );
3519 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3520 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3521 id, data );
3522 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3523 flags & MF_BYPOSITION ? pos : id,
3524 flags & ~MF_REMOVE );
3525 /* Default: MF_INSERT */
3526 return InsertMenuA( hMenu, pos, flags, id, data );
3530 /*******************************************************************
3531 * ChangeMenuW (USER32.@)
3533 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3534 UINT id, UINT flags )
3536 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3537 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3538 id, data );
3539 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3540 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3541 id, data );
3542 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3543 flags & MF_BYPOSITION ? pos : id,
3544 flags & ~MF_REMOVE );
3545 /* Default: MF_INSERT */
3546 return InsertMenuW( hMenu, pos, flags, id, data );
3550 /*******************************************************************
3551 * CheckMenuItem (USER32.@)
3553 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3555 MENUITEM *item;
3556 DWORD ret;
3558 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3559 ret = item->fState & MF_CHECKED;
3560 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3561 else item->fState &= ~MF_CHECKED;
3562 return ret;
3566 /**********************************************************************
3567 * EnableMenuItem (USER32.@)
3569 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3571 UINT oldflags;
3572 MENUITEM *item;
3573 POPUPMENU *menu;
3575 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3577 /* Get the Popupmenu to access the owner menu */
3578 if (!(menu = MENU_GetMenu(hMenu)))
3579 return (UINT)-1;
3581 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3582 return (UINT)-1;
3584 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3585 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3587 /* If the close item in the system menu change update the close button */
3588 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3590 if (menu->hSysMenuOwner != 0)
3592 RECT rc;
3593 POPUPMENU* parentMenu;
3595 /* Get the parent menu to access*/
3596 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3597 return (UINT)-1;
3599 /* Refresh the frame to reflect the change */
3600 GetWindowRect(parentMenu->hWnd, &rc);
3601 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3602 rc.bottom = 0;
3603 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3607 return oldflags;
3611 /*******************************************************************
3612 * GetMenuStringA (USER32.@)
3614 INT WINAPI GetMenuStringA(
3615 HMENU hMenu, /* [in] menuhandle */
3616 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3617 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3618 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3619 UINT wFlags /* [in] MF_ flags */
3621 MENUITEM *item;
3623 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3624 if (str && nMaxSiz) str[0] = '\0';
3625 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3626 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3627 return 0;
3629 if (!item->text) return 0;
3630 if (!str || !nMaxSiz) return strlenW(item->text);
3631 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3632 str[nMaxSiz-1] = 0;
3633 TRACE("returning %s\n", debugstr_a(str));
3634 return strlen(str);
3638 /*******************************************************************
3639 * GetMenuStringW (USER32.@)
3641 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3642 LPWSTR str, INT nMaxSiz, UINT wFlags )
3644 MENUITEM *item;
3646 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3647 if (str && nMaxSiz) str[0] = '\0';
3648 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3649 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3650 return 0;
3652 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3653 if( !(item->text)) {
3654 str[0] = 0;
3655 return 0;
3657 lstrcpynW( str, item->text, nMaxSiz );
3658 TRACE("returning %s\n", debugstr_w(str));
3659 return strlenW(str);
3663 /**********************************************************************
3664 * HiliteMenuItem (USER32.@)
3666 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3667 UINT wHilite )
3669 LPPOPUPMENU menu;
3670 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3671 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3672 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3673 if (menu->FocusedItem == wItemID) return TRUE;
3674 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3675 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3676 return TRUE;
3680 /**********************************************************************
3681 * GetMenuState (USER32.@)
3683 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3685 MENUITEM *item;
3686 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3687 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3688 debug_print_menuitem (" item: ", item, "");
3689 if (item->fType & MF_POPUP)
3691 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3692 if (!menu) return -1;
3693 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3695 else
3697 /* We used to (from way back then) mask the result to 0xff. */
3698 /* I don't know why and it seems wrong as the documented */
3699 /* return flag MF_SEPARATOR is outside that mask. */
3700 return (item->fType | item->fState);
3705 /**********************************************************************
3706 * GetMenuItemCount (USER32.@)
3708 INT WINAPI GetMenuItemCount( HMENU hMenu )
3710 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3711 if (!menu) return -1;
3712 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3713 return menu->nItems;
3717 /**********************************************************************
3718 * GetMenuItemID (USER32.@)
3720 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3722 MENUITEM * lpmi;
3724 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3725 if (lpmi->fType & MF_POPUP) return -1;
3726 return lpmi->wID;
3731 /*******************************************************************
3732 * InsertMenuW (USER32.@)
3734 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3735 UINT_PTR id, LPCWSTR str )
3737 MENUITEM *item;
3739 if (IS_STRING_ITEM(flags) && str)
3740 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3741 hMenu, pos, flags, id, debugstr_w(str) );
3742 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3743 hMenu, pos, flags, id, str );
3745 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3747 if (!(MENU_SetItemData( item, flags, id, str )))
3749 RemoveMenu( hMenu, pos, flags );
3750 return FALSE;
3753 item->hCheckBit = item->hUnCheckBit = 0;
3754 return TRUE;
3758 /*******************************************************************
3759 * InsertMenuA (USER32.@)
3761 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3762 UINT_PTR id, LPCSTR str )
3764 BOOL ret = FALSE;
3766 if (IS_STRING_ITEM(flags) && str)
3768 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3769 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3770 if (newstr)
3772 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3773 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3774 HeapFree( GetProcessHeap(), 0, newstr );
3776 return ret;
3778 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3782 /*******************************************************************
3783 * AppendMenuA (USER32.@)
3785 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3786 UINT_PTR id, LPCSTR data )
3788 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3792 /*******************************************************************
3793 * AppendMenuW (USER32.@)
3795 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3796 UINT_PTR id, LPCWSTR data )
3798 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3802 /**********************************************************************
3803 * RemoveMenu (USER32.@)
3805 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3807 LPPOPUPMENU menu;
3808 MENUITEM *item;
3810 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3811 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3812 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3814 /* Remove item */
3816 MENU_FreeItemData( item );
3818 if (--menu->nItems == 0)
3820 HeapFree( GetProcessHeap(), 0, menu->items );
3821 menu->items = NULL;
3823 else
3825 while(nPos < menu->nItems)
3827 *item = *(item+1);
3828 item++;
3829 nPos++;
3831 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3832 menu->nItems * sizeof(MENUITEM) );
3834 return TRUE;
3838 /**********************************************************************
3839 * DeleteMenu (USER32.@)
3841 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3843 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3844 if (!item) return FALSE;
3845 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3846 /* nPos is now the position of the item */
3847 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3848 return TRUE;
3852 /*******************************************************************
3853 * ModifyMenuW (USER32.@)
3855 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3856 UINT_PTR id, LPCWSTR str )
3858 MENUITEM *item;
3860 if (IS_STRING_ITEM(flags))
3861 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3862 else
3863 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3865 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3866 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3867 return MENU_SetItemData( item, flags, id, str );
3871 /*******************************************************************
3872 * ModifyMenuA (USER32.@)
3874 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3875 UINT_PTR id, LPCSTR str )
3877 BOOL ret = FALSE;
3879 if (IS_STRING_ITEM(flags) && str)
3881 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3882 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3883 if (newstr)
3885 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3886 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3887 HeapFree( GetProcessHeap(), 0, newstr );
3889 return ret;
3891 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3895 /**********************************************************************
3896 * CreatePopupMenu (USER32.@)
3898 HMENU WINAPI CreatePopupMenu(void)
3900 HMENU hmenu;
3901 POPUPMENU *menu;
3903 if (!(hmenu = CreateMenu())) return 0;
3904 menu = MENU_GetMenu( hmenu );
3905 menu->wFlags |= MF_POPUP;
3906 menu->bTimeToHide = FALSE;
3907 return hmenu;
3911 /**********************************************************************
3912 * GetMenuCheckMarkDimensions (USER.417)
3913 * GetMenuCheckMarkDimensions (USER32.@)
3915 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3917 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3921 /**********************************************************************
3922 * SetMenuItemBitmaps (USER32.@)
3924 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3925 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3927 MENUITEM *item;
3929 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3931 if (!hNewCheck && !hNewUnCheck)
3933 item->fState &= ~MF_USECHECKBITMAPS;
3935 else /* Install new bitmaps */
3937 item->hCheckBit = hNewCheck;
3938 item->hUnCheckBit = hNewUnCheck;
3939 item->fState |= MF_USECHECKBITMAPS;
3941 return TRUE;
3945 /**********************************************************************
3946 * CreateMenu (USER32.@)
3948 HMENU WINAPI CreateMenu(void)
3950 HMENU hMenu;
3951 LPPOPUPMENU menu;
3952 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3953 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3955 ZeroMemory(menu, sizeof(POPUPMENU));
3956 menu->wMagic = MENU_MAGIC;
3957 menu->FocusedItem = NO_SELECTED_ITEM;
3958 menu->bTimeToHide = FALSE;
3960 TRACE("return %p\n", hMenu );
3962 return hMenu;
3966 /**********************************************************************
3967 * DestroyMenu (USER32.@)
3969 BOOL WINAPI DestroyMenu( HMENU hMenu )
3971 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3973 TRACE("(%p)\n", hMenu);
3976 if (!lppop) return FALSE;
3978 lppop->wMagic = 0; /* Mark it as destroyed */
3980 /* DestroyMenu should not destroy system menu popup owner */
3981 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3983 DestroyWindow( lppop->hWnd );
3984 lppop->hWnd = 0;
3987 if (lppop->items) /* recursively destroy submenus */
3989 int i;
3990 MENUITEM *item = lppop->items;
3991 for (i = lppop->nItems; i > 0; i--, item++)
3993 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3994 MENU_FreeItemData( item );
3996 HeapFree( GetProcessHeap(), 0, lppop->items );
3998 USER_HEAP_FREE( hMenu );
3999 return TRUE;
4003 /**********************************************************************
4004 * GetSystemMenu (USER32.@)
4006 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4008 WND *wndPtr = WIN_GetPtr( hWnd );
4009 HMENU retvalue = 0;
4011 if (wndPtr == WND_DESKTOP) return 0;
4012 if (wndPtr == WND_OTHER_PROCESS)
4014 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4016 else if (wndPtr)
4018 if (wndPtr->hSysMenu && bRevert)
4020 DestroyMenu(wndPtr->hSysMenu);
4021 wndPtr->hSysMenu = 0;
4024 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4025 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4027 if( wndPtr->hSysMenu )
4029 POPUPMENU *menu;
4030 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4032 /* Store the dummy sysmenu handle to facilitate the refresh */
4033 /* of the close button if the SC_CLOSE item change */
4034 menu = MENU_GetMenu(retvalue);
4035 if ( menu )
4036 menu->hSysMenuOwner = wndPtr->hSysMenu;
4038 WIN_ReleasePtr( wndPtr );
4040 return bRevert ? 0 : retvalue;
4044 /*******************************************************************
4045 * SetSystemMenu (USER32.@)
4047 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4049 WND *wndPtr = WIN_GetPtr( hwnd );
4051 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4053 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4054 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4055 WIN_ReleasePtr( wndPtr );
4056 return TRUE;
4058 return FALSE;
4062 /**********************************************************************
4063 * GetMenu (USER32.@)
4065 HMENU WINAPI GetMenu( HWND hWnd )
4067 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4068 TRACE("for %p returning %p\n", hWnd, retvalue);
4069 return retvalue;
4072 /**********************************************************************
4073 * GetMenuBarInfo (USER32.@)
4075 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4077 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4078 return FALSE;
4081 /**********************************************************************
4082 * MENU_SetMenu
4084 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4085 * SetWindowPos call that would result if SetMenu were called directly.
4087 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4089 TRACE("(%p, %p);\n", hWnd, hMenu);
4091 if (hMenu && !IsMenu(hMenu))
4093 WARN("hMenu %p is not a menu handle\n", hMenu);
4094 return FALSE;
4096 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4097 return FALSE;
4099 hWnd = WIN_GetFullHandle( hWnd );
4100 if (GetCapture() == hWnd)
4101 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4103 if (hMenu != 0)
4105 LPPOPUPMENU lpmenu;
4107 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4109 lpmenu->hWnd = hWnd;
4110 lpmenu->Height = 0; /* Make sure we recalculate the size */
4112 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4113 return TRUE;
4117 /**********************************************************************
4118 * SetMenu (USER32.@)
4120 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4122 if(!MENU_SetMenu(hWnd, hMenu))
4123 return FALSE;
4125 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4126 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4127 return TRUE;
4131 /**********************************************************************
4132 * GetSubMenu (USER32.@)
4134 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4136 MENUITEM * lpmi;
4138 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4139 if (!(lpmi->fType & MF_POPUP)) return 0;
4140 return lpmi->hSubMenu;
4144 /**********************************************************************
4145 * DrawMenuBar (USER32.@)
4147 BOOL WINAPI DrawMenuBar( HWND hWnd )
4149 LPPOPUPMENU lppop;
4150 HMENU hMenu = GetMenu(hWnd);
4152 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4153 return FALSE;
4154 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4156 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4157 lppop->hwndOwner = hWnd;
4158 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4159 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4160 return TRUE;
4163 /***********************************************************************
4164 * DrawMenuBarTemp (USER32.@)
4166 * UNDOCUMENTED !!
4168 * called by W98SE desk.cpl Control Panel Applet
4170 * Not 100% sure about the param names, but close.
4172 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4174 LPPOPUPMENU lppop;
4175 UINT i,retvalue;
4176 HFONT hfontOld = 0;
4177 BOOL flat_menu = FALSE;
4179 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4181 if (!hMenu)
4182 hMenu = GetMenu(hwnd);
4184 if (!hFont)
4185 hFont = get_menu_font(FALSE);
4187 lppop = MENU_GetMenu( hMenu );
4188 if (lppop == NULL || lprect == NULL)
4190 retvalue = GetSystemMetrics(SM_CYMENU);
4191 goto END;
4194 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4196 hfontOld = SelectObject( hDC, hFont);
4198 if (lppop->Height == 0)
4199 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4201 lprect->bottom = lprect->top + lppop->Height;
4203 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4205 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4206 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4207 LineTo( hDC, lprect->right, lprect->bottom );
4209 if (lppop->nItems == 0)
4211 retvalue = GetSystemMetrics(SM_CYMENU);
4212 goto END;
4215 for (i = 0; i < lppop->nItems; i++)
4217 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4218 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4220 retvalue = lppop->Height;
4222 END:
4223 if (hfontOld) SelectObject (hDC, hfontOld);
4224 return retvalue;
4227 /***********************************************************************
4228 * EndMenu (USER.187)
4229 * EndMenu (USER32.@)
4231 BOOL WINAPI EndMenu(void)
4233 /* if we are in the menu code, and it is active */
4234 if (!fEndMenu && top_popup)
4236 /* terminate the menu handling code */
4237 fEndMenu = TRUE;
4239 /* needs to be posted to wakeup the internal menu handler */
4240 /* which will now terminate the menu, in the event that */
4241 /* the main window was minimized, or lost focus, so we */
4242 /* don't end up with an orphaned menu */
4243 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4245 return fEndMenu;
4249 /***********************************************************************
4250 * LookupMenuHandle (USER.217)
4252 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4254 HMENU hmenu32 = HMENU_32(hmenu);
4255 UINT id32 = id;
4256 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4257 else return HMENU_16(hmenu32);
4261 /**********************************************************************
4262 * LoadMenu (USER.150)
4264 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4266 HRSRC16 hRsrc;
4267 HGLOBAL16 handle;
4268 HMENU16 hMenu;
4270 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4271 if (!name) return 0;
4273 instance = GetExePtr( instance );
4274 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4275 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4276 hMenu = LoadMenuIndirect16(LockResource16(handle));
4277 FreeResource16( handle );
4278 return hMenu;
4282 /*****************************************************************
4283 * LoadMenuA (USER32.@)
4285 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4287 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4288 if (!hrsrc) return 0;
4289 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4293 /*****************************************************************
4294 * LoadMenuW (USER32.@)
4296 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4298 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4299 if (!hrsrc) return 0;
4300 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4304 /**********************************************************************
4305 * LoadMenuIndirect (USER.220)
4307 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4309 HMENU hMenu;
4310 WORD version, offset;
4311 LPCSTR p = (LPCSTR)template;
4313 TRACE("(%p)\n", template );
4314 version = GET_WORD(p);
4315 p += sizeof(WORD);
4316 if (version)
4318 WARN("version must be 0 for Win16\n" );
4319 return 0;
4321 offset = GET_WORD(p);
4322 p += sizeof(WORD) + offset;
4323 if (!(hMenu = CreateMenu())) return 0;
4324 if (!MENU_ParseResource( p, hMenu, FALSE ))
4326 DestroyMenu( hMenu );
4327 return 0;
4329 return HMENU_16(hMenu);
4333 /**********************************************************************
4334 * LoadMenuIndirectW (USER32.@)
4336 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4338 HMENU hMenu;
4339 WORD version, offset;
4340 LPCSTR p = (LPCSTR)template;
4342 version = GET_WORD(p);
4343 p += sizeof(WORD);
4344 TRACE("%p, ver %d\n", template, version );
4345 switch (version)
4347 case 0: /* standard format is version of 0 */
4348 offset = GET_WORD(p);
4349 p += sizeof(WORD) + offset;
4350 if (!(hMenu = CreateMenu())) return 0;
4351 if (!MENU_ParseResource( p, hMenu, TRUE ))
4353 DestroyMenu( hMenu );
4354 return 0;
4356 return hMenu;
4357 case 1: /* extended format is version of 1 */
4358 offset = GET_WORD(p);
4359 p += sizeof(WORD) + offset;
4360 if (!(hMenu = CreateMenu())) return 0;
4361 if (!MENUEX_ParseResource( p, hMenu))
4363 DestroyMenu( hMenu );
4364 return 0;
4366 return hMenu;
4367 default:
4368 ERR("version %d not supported.\n", version);
4369 return 0;
4374 /**********************************************************************
4375 * LoadMenuIndirectA (USER32.@)
4377 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4379 return LoadMenuIndirectW( template );
4383 /**********************************************************************
4384 * IsMenu (USER32.@)
4386 BOOL WINAPI IsMenu(HMENU hmenu)
4388 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4390 if (!menu)
4392 SetLastError(ERROR_INVALID_MENU_HANDLE);
4393 return FALSE;
4395 return TRUE;
4398 /**********************************************************************
4399 * GetMenuItemInfo_common
4402 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4403 LPMENUITEMINFOW lpmii, BOOL unicode)
4405 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4407 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4409 if (!menu)
4410 return FALSE;
4412 if( lpmii->fMask & MIIM_TYPE) {
4413 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4414 WARN("invalid combination of fMask bits used\n");
4415 /* this does not happen on Win9x/ME */
4416 SetLastError( ERROR_INVALID_PARAMETER);
4417 return FALSE;
4419 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4420 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4421 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4422 if( lpmii->fType & MFT_BITMAP) {
4423 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4424 lpmii->cch = 0;
4425 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4426 /* this does not happen on Win9x/ME */
4427 lpmii->dwTypeData = 0;
4428 lpmii->cch = 0;
4432 /* copy the text string */
4433 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4434 if( !menu->text ) {
4435 if(lpmii->dwTypeData && lpmii->cch) {
4436 lpmii->cch = 0;
4437 if( unicode)
4438 *((WCHAR *)lpmii->dwTypeData) = 0;
4439 else
4440 *((CHAR *)lpmii->dwTypeData) = 0;
4442 } else {
4443 int len;
4444 if (unicode)
4446 len = strlenW(menu->text);
4447 if(lpmii->dwTypeData && lpmii->cch)
4448 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4450 else
4452 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4453 0, NULL, NULL ) - 1;
4454 if(lpmii->dwTypeData && lpmii->cch)
4455 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4456 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4457 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4459 /* if we've copied a substring we return its length */
4460 if(lpmii->dwTypeData && lpmii->cch)
4461 if (lpmii->cch <= len + 1)
4462 lpmii->cch--;
4463 else
4464 lpmii->cch = len;
4465 else {
4466 /* return length of string */
4467 /* not on Win9x/ME if fType & MFT_BITMAP */
4468 lpmii->cch = len;
4473 if (lpmii->fMask & MIIM_FTYPE)
4474 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4476 if (lpmii->fMask & MIIM_BITMAP)
4477 lpmii->hbmpItem = menu->hbmpItem;
4479 if (lpmii->fMask & MIIM_STATE)
4480 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4482 if (lpmii->fMask & MIIM_ID)
4483 lpmii->wID = menu->wID;
4485 if (lpmii->fMask & MIIM_SUBMENU)
4486 lpmii->hSubMenu = menu->hSubMenu;
4487 else {
4488 /* hSubMenu is always cleared
4489 * (not on Win9x/ME ) */
4490 lpmii->hSubMenu = 0;
4493 if (lpmii->fMask & MIIM_CHECKMARKS) {
4494 lpmii->hbmpChecked = menu->hCheckBit;
4495 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4497 if (lpmii->fMask & MIIM_DATA)
4498 lpmii->dwItemData = menu->dwItemData;
4500 return TRUE;
4503 /**********************************************************************
4504 * GetMenuItemInfoA (USER32.@)
4506 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4507 LPMENUITEMINFOA lpmii)
4509 BOOL ret;
4510 MENUITEMINFOA mii;
4511 if( lpmii->cbSize != sizeof( mii) &&
4512 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4513 SetLastError( ERROR_INVALID_PARAMETER);
4514 return FALSE;
4516 memcpy( &mii, lpmii, lpmii->cbSize);
4517 mii.cbSize = sizeof( mii);
4518 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4519 (LPMENUITEMINFOW)&mii, FALSE);
4520 mii.cbSize = lpmii->cbSize;
4521 memcpy( lpmii, &mii, mii.cbSize);
4522 return ret;
4525 /**********************************************************************
4526 * GetMenuItemInfoW (USER32.@)
4528 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4529 LPMENUITEMINFOW lpmii)
4531 BOOL ret;
4532 MENUITEMINFOW mii;
4533 if( lpmii->cbSize != sizeof( mii) &&
4534 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4535 SetLastError( ERROR_INVALID_PARAMETER);
4536 return FALSE;
4538 memcpy( &mii, lpmii, lpmii->cbSize);
4539 mii.cbSize = sizeof( mii);
4540 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4541 mii.cbSize = lpmii->cbSize;
4542 memcpy( lpmii, &mii, mii.cbSize);
4543 return ret;
4547 /* set a menu item text from a ASCII or Unicode string */
4548 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4550 if (!text)
4551 menu->text = NULL;
4552 else if (unicode)
4554 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4555 strcpyW( menu->text, text );
4557 else
4559 LPCSTR str = (LPCSTR)text;
4560 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4561 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4562 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4567 /**********************************************************************
4568 * SetMenuItemInfo_common
4571 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4572 const MENUITEMINFOW *lpmii,
4573 BOOL unicode)
4575 if (!menu) return FALSE;
4577 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4579 if (lpmii->fMask & MIIM_TYPE ) {
4580 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4581 WARN("invalid combination of fMask bits used\n");
4582 /* this does not happen on Win9x/ME */
4583 SetLastError( ERROR_INVALID_PARAMETER);
4584 return FALSE;
4587 /* Remove the old type bits and replace them with the new ones */
4588 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4589 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4591 if (IS_STRING_ITEM(menu->fType)) {
4592 HeapFree(GetProcessHeap(), 0, menu->text);
4593 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4594 } else if( (menu->fType) & MFT_BITMAP)
4595 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4598 if (lpmii->fMask & MIIM_FTYPE ) {
4599 if(( lpmii->fType & MFT_BITMAP)) {
4600 SetLastError( ERROR_INVALID_PARAMETER);
4601 return FALSE;
4603 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4604 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4606 if (lpmii->fMask & MIIM_STRING ) {
4607 /* free the string when used */
4608 HeapFree(GetProcessHeap(), 0, menu->text);
4609 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4612 if (lpmii->fMask & MIIM_STATE)
4614 /* Other menu items having MFS_DEFAULT are not converted
4615 to normal items */
4616 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4619 if (lpmii->fMask & MIIM_ID)
4620 menu->wID = lpmii->wID;
4622 if (lpmii->fMask & MIIM_SUBMENU) {
4623 menu->hSubMenu = lpmii->hSubMenu;
4624 if (menu->hSubMenu) {
4625 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4626 if (subMenu) {
4627 subMenu->wFlags |= MF_POPUP;
4628 menu->fType |= MF_POPUP;
4630 else {
4631 SetLastError( ERROR_INVALID_PARAMETER);
4632 return FALSE;
4635 else
4636 menu->fType &= ~MF_POPUP;
4639 if (lpmii->fMask & MIIM_CHECKMARKS)
4641 menu->hCheckBit = lpmii->hbmpChecked;
4642 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4644 if (lpmii->fMask & MIIM_DATA)
4645 menu->dwItemData = lpmii->dwItemData;
4647 if (lpmii->fMask & MIIM_BITMAP)
4648 menu->hbmpItem = lpmii->hbmpItem;
4650 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4651 menu->fType |= MFT_SEPARATOR;
4653 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4654 return TRUE;
4657 /**********************************************************************
4658 * SetMenuItemInfoA (USER32.@)
4660 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4661 const MENUITEMINFOA *lpmii)
4663 MENUITEMINFOA mii;
4665 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4667 if( lpmii->cbSize != sizeof( mii) &&
4668 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4669 SetLastError( ERROR_INVALID_PARAMETER);
4670 return FALSE;
4672 memcpy( &mii, lpmii, lpmii->cbSize);
4673 if( lpmii->cbSize != sizeof( mii)) {
4674 mii.cbSize = sizeof( mii);
4675 mii.hbmpItem = NULL;
4677 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4678 (const MENUITEMINFOW *)&mii, FALSE);
4681 /**********************************************************************
4682 * SetMenuItemInfoW (USER32.@)
4684 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4685 const MENUITEMINFOW *lpmii)
4687 MENUITEMINFOW mii;
4689 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4691 if( lpmii->cbSize != sizeof( mii) &&
4692 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4693 SetLastError( ERROR_INVALID_PARAMETER);
4694 return FALSE;
4696 memcpy( &mii, lpmii, lpmii->cbSize);
4697 if( lpmii->cbSize != sizeof( mii)) {
4698 mii.cbSize = sizeof( mii);
4699 mii.hbmpItem = NULL;
4701 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4702 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4705 /**********************************************************************
4706 * SetMenuDefaultItem (USER32.@)
4709 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4711 UINT i;
4712 POPUPMENU *menu;
4713 MENUITEM *item;
4715 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4717 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4719 /* reset all default-item flags */
4720 item = menu->items;
4721 for (i = 0; i < menu->nItems; i++, item++)
4723 item->fState &= ~MFS_DEFAULT;
4726 /* no default item */
4727 if ( -1 == uItem)
4729 return TRUE;
4732 item = menu->items;
4733 if ( bypos )
4735 if ( uItem >= menu->nItems ) return FALSE;
4736 item[uItem].fState |= MFS_DEFAULT;
4737 return TRUE;
4739 else
4741 for (i = 0; i < menu->nItems; i++, item++)
4743 if (item->wID == uItem)
4745 item->fState |= MFS_DEFAULT;
4746 return TRUE;
4751 return FALSE;
4754 /**********************************************************************
4755 * GetMenuDefaultItem (USER32.@)
4757 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4759 POPUPMENU *menu;
4760 MENUITEM * item;
4761 UINT i = 0;
4763 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4765 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4767 /* find default item */
4768 item = menu->items;
4770 /* empty menu */
4771 if (! item) return -1;
4773 while ( !( item->fState & MFS_DEFAULT ) )
4775 i++; item++;
4776 if (i >= menu->nItems ) return -1;
4779 /* default: don't return disabled items */
4780 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4782 /* search rekursiv when needed */
4783 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4785 UINT ret;
4786 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4787 if ( -1 != ret ) return ret;
4789 /* when item not found in submenu, return the popup item */
4791 return ( bypos ) ? i : item->wID;
4796 /**********************************************************************
4797 * InsertMenuItemA (USER32.@)
4799 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4800 const MENUITEMINFOA *lpmii)
4802 MENUITEM *item;
4803 MENUITEMINFOA mii;
4805 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4807 if( lpmii->cbSize != sizeof( mii) &&
4808 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4809 SetLastError( ERROR_INVALID_PARAMETER);
4810 return FALSE;
4812 memcpy( &mii, lpmii, lpmii->cbSize);
4813 if( lpmii->cbSize != sizeof( mii)) {
4814 mii.cbSize = sizeof( mii);
4815 mii.hbmpItem = NULL;
4818 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4819 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4823 /**********************************************************************
4824 * InsertMenuItemW (USER32.@)
4826 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4827 const MENUITEMINFOW *lpmii)
4829 MENUITEM *item;
4830 MENUITEMINFOW mii;
4832 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4834 if( lpmii->cbSize != sizeof( mii) &&
4835 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4836 SetLastError( ERROR_INVALID_PARAMETER);
4837 return FALSE;
4839 memcpy( &mii, lpmii, lpmii->cbSize);
4840 if( lpmii->cbSize != sizeof( mii)) {
4841 mii.cbSize = sizeof( mii);
4842 mii.hbmpItem = NULL;
4845 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4846 return SetMenuItemInfo_common(item, &mii, TRUE);
4849 /**********************************************************************
4850 * CheckMenuRadioItem (USER32.@)
4853 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4854 UINT first, UINT last, UINT check,
4855 UINT bypos)
4857 BOOL done = FALSE;
4858 UINT i;
4859 MENUITEM *mi_first = NULL, *mi_check;
4860 HMENU m_first, m_check;
4862 for (i = first; i <= last; i++)
4864 UINT pos = i;
4866 if (!mi_first)
4868 m_first = hMenu;
4869 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4870 if (!mi_first) continue;
4871 mi_check = mi_first;
4872 m_check = m_first;
4874 else
4876 m_check = hMenu;
4877 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4878 if (!mi_check) continue;
4881 if (m_first != m_check) continue;
4882 if (mi_check->fType == MFT_SEPARATOR) continue;
4884 if (i == check)
4886 mi_check->fType |= MFT_RADIOCHECK;
4887 mi_check->fState |= MFS_CHECKED;
4888 done = TRUE;
4890 else
4892 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4893 mi_check->fState &= ~MFS_CHECKED;
4897 return done;
4901 /**********************************************************************
4902 * GetMenuItemRect (USER32.@)
4904 * ATTENTION: Here, the returned values in rect are the screen
4905 * coordinates of the item just like if the menu was
4906 * always on the upper left side of the application.
4909 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4910 LPRECT rect)
4912 POPUPMENU *itemMenu;
4913 MENUITEM *item;
4914 HWND referenceHwnd;
4916 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4918 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4919 referenceHwnd = hwnd;
4921 if(!hwnd)
4923 itemMenu = MENU_GetMenu(hMenu);
4924 if (itemMenu == NULL)
4925 return FALSE;
4927 if(itemMenu->hWnd == 0)
4928 return FALSE;
4929 referenceHwnd = itemMenu->hWnd;
4932 if ((rect == NULL) || (item == NULL))
4933 return FALSE;
4935 *rect = item->rect;
4937 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4939 return TRUE;
4943 /**********************************************************************
4944 * SetMenuInfo (USER32.@)
4946 * FIXME
4947 * MIM_APPLYTOSUBMENUS
4948 * actually use the items to draw the menu
4950 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4952 POPUPMENU *menu;
4954 TRACE("(%p %p)\n", hMenu, lpmi);
4956 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4959 if (lpmi->fMask & MIM_BACKGROUND)
4960 menu->hbrBack = lpmi->hbrBack;
4962 if (lpmi->fMask & MIM_HELPID)
4963 menu->dwContextHelpID = lpmi->dwContextHelpID;
4965 if (lpmi->fMask & MIM_MAXHEIGHT)
4966 menu->cyMax = lpmi->cyMax;
4968 if (lpmi->fMask & MIM_MENUDATA)
4969 menu->dwMenuData = lpmi->dwMenuData;
4971 if (lpmi->fMask & MIM_STYLE)
4973 menu->dwStyle = lpmi->dwStyle;
4974 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4975 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4976 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4977 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
4980 return TRUE;
4982 return FALSE;
4985 /**********************************************************************
4986 * GetMenuInfo (USER32.@)
4988 * NOTES
4989 * win98/NT5.0
4992 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4993 { POPUPMENU *menu;
4995 TRACE("(%p %p)\n", hMenu, lpmi);
4997 if (lpmi && (menu = MENU_GetMenu(hMenu)))
5000 if (lpmi->fMask & MIM_BACKGROUND)
5001 lpmi->hbrBack = menu->hbrBack;
5003 if (lpmi->fMask & MIM_HELPID)
5004 lpmi->dwContextHelpID = menu->dwContextHelpID;
5006 if (lpmi->fMask & MIM_MAXHEIGHT)
5007 lpmi->cyMax = menu->cyMax;
5009 if (lpmi->fMask & MIM_MENUDATA)
5010 lpmi->dwMenuData = menu->dwMenuData;
5012 if (lpmi->fMask & MIM_STYLE)
5013 lpmi->dwStyle = menu->dwStyle;
5015 return TRUE;
5017 return FALSE;
5021 /**********************************************************************
5022 * SetMenuContextHelpId (USER32.@)
5024 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5026 LPPOPUPMENU menu;
5028 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5030 if ((menu = MENU_GetMenu(hMenu)))
5032 menu->dwContextHelpID = dwContextHelpID;
5033 return TRUE;
5035 return FALSE;
5039 /**********************************************************************
5040 * GetMenuContextHelpId (USER32.@)
5042 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5044 LPPOPUPMENU menu;
5046 TRACE("(%p)\n", hMenu);
5048 if ((menu = MENU_GetMenu(hMenu)))
5050 return menu->dwContextHelpID;
5052 return 0;
5055 /**********************************************************************
5056 * MenuItemFromPoint (USER32.@)
5058 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5060 POPUPMENU *menu = MENU_GetMenu(hMenu);
5061 UINT pos;
5063 /*FIXME: Do we have to handle hWnd here? */
5064 if (!menu) return -1;
5065 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5066 return pos;
5070 /**********************************************************************
5071 * translate_accelerator
5073 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5074 BYTE fVirt, WORD key, WORD cmd )
5076 INT mask = 0;
5077 UINT mesg = 0;
5079 if (wParam != key) return FALSE;
5081 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5082 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5083 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5085 if (message == WM_CHAR || message == WM_SYSCHAR)
5087 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5089 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5090 goto found;
5093 else
5095 if(fVirt & FVIRTKEY)
5097 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5098 wParam, 0xff & HIWORD(lParam));
5100 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5101 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5103 else
5105 if (!(lParam & 0x01000000)) /* no special_key */
5107 if ((fVirt & FALT) && (lParam & 0x20000000))
5108 { /* ^^ ALT pressed */
5109 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5110 goto found;
5115 return FALSE;
5117 found:
5118 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5119 mesg = 1;
5120 else
5122 HMENU hMenu, hSubMenu, hSysMenu;
5123 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5125 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5126 hSysMenu = get_win_sys_menu( hWnd );
5128 /* find menu item and ask application to initialize it */
5129 /* 1. in the system menu */
5130 hSubMenu = hSysMenu;
5131 nPos = cmd;
5132 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5134 if (GetCapture())
5135 mesg = 2;
5136 if (!IsWindowEnabled(hWnd))
5137 mesg = 3;
5138 else
5140 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5141 if(hSubMenu != hSysMenu)
5143 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5144 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5145 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5147 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5150 else /* 2. in the window's menu */
5152 hSubMenu = hMenu;
5153 nPos = cmd;
5154 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5156 if (GetCapture())
5157 mesg = 2;
5158 if (!IsWindowEnabled(hWnd))
5159 mesg = 3;
5160 else
5162 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5163 if(hSubMenu != hMenu)
5165 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5166 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5167 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5169 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5174 if (mesg == 0)
5176 if (uSysStat != (UINT)-1)
5178 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5179 mesg=4;
5180 else
5181 mesg=WM_SYSCOMMAND;
5183 else
5185 if (uStat != (UINT)-1)
5187 if (IsIconic(hWnd))
5188 mesg=5;
5189 else
5191 if (uStat & (MF_DISABLED|MF_GRAYED))
5192 mesg=6;
5193 else
5194 mesg=WM_COMMAND;
5197 else
5198 mesg=WM_COMMAND;
5203 if( mesg==WM_COMMAND )
5205 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5206 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5208 else if( mesg==WM_SYSCOMMAND )
5210 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5211 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5213 else
5215 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5216 * #0: unknown (please report!)
5217 * #1: for WM_KEYUP,WM_SYSKEYUP
5218 * #2: mouse is captured
5219 * #3: window is disabled
5220 * #4: it's a disabled system menu option
5221 * #5: it's a menu option, but window is iconic
5222 * #6: it's a menu option, but disabled
5224 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5225 if(mesg==0)
5226 ERR_(accel)(" unknown reason - please report!\n");
5228 return TRUE;
5231 /**********************************************************************
5232 * TranslateAcceleratorA (USER32.@)
5233 * TranslateAccelerator (USER32.@)
5235 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5237 /* YES, Accel16! */
5238 LPACCEL16 lpAccelTbl;
5239 int i;
5240 WPARAM wParam;
5242 if (!hWnd || !msg) return 0;
5244 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5246 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5247 return 0;
5250 wParam = msg->wParam;
5252 switch (msg->message)
5254 case WM_KEYDOWN:
5255 case WM_SYSKEYDOWN:
5256 break;
5258 case WM_CHAR:
5259 case WM_SYSCHAR:
5261 char ch = LOWORD(wParam);
5262 WCHAR wch;
5263 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5264 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5266 break;
5268 default:
5269 return 0;
5272 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5273 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5274 i = 0;
5277 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5278 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5279 return 1;
5280 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5282 return 0;
5285 /**********************************************************************
5286 * TranslateAcceleratorW (USER32.@)
5288 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5290 /* YES, Accel16! */
5291 LPACCEL16 lpAccelTbl;
5292 int i;
5294 if (!hWnd || !msg) return 0;
5296 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5298 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5299 return 0;
5302 switch (msg->message)
5304 case WM_KEYDOWN:
5305 case WM_SYSKEYDOWN:
5306 case WM_CHAR:
5307 case WM_SYSCHAR:
5308 break;
5310 default:
5311 return 0;
5314 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5315 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5316 i = 0;
5319 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5320 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5321 return 1;
5322 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5324 return 0;