jscript: Added script site tests.
[wine/multimedia.git] / dlls / user32 / menu.c
blob058cf0fee6f3703317b3fc3966c2e4b65386c5d9
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
56 #include "wownt32.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "win.h"
60 #include "controls.h"
61 #include "user_private.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(menu);
65 WINE_DECLARE_DEBUG_CHANNEL(accel);
67 /* internal popup menu window messages */
69 #define MM_SETMENUHANDLE (WM_USER + 0)
70 #define MM_GETMENUHANDLE (WM_USER + 1)
72 /* Menu item structure */
73 typedef struct {
74 /* ----------- MENUITEMINFO Stuff ----------- */
75 UINT fType; /* Item type. */
76 UINT fState; /* Item state. */
77 UINT_PTR wID; /* Item id. */
78 HMENU hSubMenu; /* Pop-up menu. */
79 HBITMAP hCheckBit; /* Bitmap when checked. */
80 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
81 LPWSTR text; /* Item text. */
82 ULONG_PTR dwItemData; /* Application defined. */
83 LPWSTR dwTypeData; /* depends on fMask */
84 HBITMAP hbmpItem; /* bitmap */
85 /* ----------- Wine stuff ----------- */
86 RECT rect; /* Item area (relative to menu window) */
87 UINT xTab; /* X position of text after Tab */
88 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
89 * bitmap */
90 } MENUITEM;
92 /* Popup menu structure */
93 typedef struct {
94 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
95 WORD wMagic; /* Magic number */
96 WORD Width; /* Width of the whole menu */
97 WORD Height; /* Height of the whole menu */
98 UINT nItems; /* Number of items in the menu */
99 HWND hWnd; /* Window containing the menu */
100 MENUITEM *items; /* Array of menu items */
101 UINT FocusedItem; /* Currently focused item */
102 HWND hwndOwner; /* window receiving the messages for ownerdraw */
103 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
104 BOOL bScrolling; /* Scroll arrows are active */
105 UINT nScrollPos; /* Current scroll position */
106 UINT nTotalHeight; /* Total height of menu items inside menu */
107 /* ------------ MENUINFO members ------ */
108 DWORD dwStyle; /* Extended menu style */
109 UINT cyMax; /* max height of the whole menu, 0 is screen height */
110 HBRUSH hbrBack; /* brush for menu background */
111 DWORD dwContextHelpID;
112 DWORD dwMenuData; /* application defined value */
113 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
114 SIZE maxBmpSize; /* Maximum size of the bitmap items */
115 } POPUPMENU, *LPPOPUPMENU;
117 /* internal flags for menu tracking */
119 #define TF_ENDMENU 0x10000
120 #define TF_SUSPENDPOPUP 0x20000
121 #define TF_SKIPREMOVE 0x40000
123 typedef struct
125 UINT trackFlags;
126 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
127 HMENU hTopMenu; /* initial menu */
128 HWND hOwnerWnd; /* where notifications are sent */
129 POINT pt;
130 } MTRACKER;
132 #define MENU_MAGIC 0x554d /* 'MU' */
134 #define ITEM_PREV -1
135 #define ITEM_NEXT 1
137 /* Internal MENU_TrackMenu() flags */
138 #define TPM_INTERNAL 0xF0000000
139 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
140 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
141 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
143 /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
146 /* top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
150 /* (other menu->FocusedItem values give the position of the focused item) */
151 #define NO_SELECTED_ITEM 0xffff
153 #define MENU_ITEM_TYPE(flags) \
154 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
156 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
157 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
158 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
160 #define IS_SYSTEM_MENU(menu) \
161 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
163 #define MENUITEMINFO_TYPE_MASK \
164 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
165 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
166 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
167 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
168 #define STATE_MASK (~TYPE_MASK)
169 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
171 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
173 static SIZE menucharsize;
174 static UINT ODitemheight; /* default owner drawn item height */
176 /* Use global popup window because there's no way 2 menus can
177 * be tracked at the same time. */
178 static HWND top_popup;
180 /* Flag set by EndMenu() to force an exit from menu tracking */
181 static BOOL fEndMenu = FALSE;
183 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
185 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
187 /*********************************************************************
188 * menu class descriptor
190 const struct builtin_class_descr MENU_builtin_class =
192 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
193 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
194 NULL, /* procA (winproc is Unicode only) */
195 PopupMenuWndProc, /* procW */
196 sizeof(HMENU), /* extra */
197 IDC_ARROW, /* cursor */
198 (HBRUSH)(COLOR_MENU+1) /* brush */
202 /***********************************************************************
203 * debug_print_menuitem
205 * Print a menuitem in readable form.
208 #define debug_print_menuitem(pre, mp, post) \
209 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
211 #define MENUOUT(text) \
212 TRACE("%s%s", (count++ ? "," : ""), (text))
214 #define MENUFLAG(bit,text) \
215 do { \
216 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
217 } while (0)
219 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
220 const char *postfix)
222 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
223 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
224 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
225 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
226 TRACE("%s ", prefix);
227 if (mp) {
228 UINT flags = mp->fType;
229 TRACE( "{ ID=0x%lx", mp->wID);
230 if ( mp->hSubMenu)
231 TRACE( ", Sub=%p", mp->hSubMenu);
232 if (flags) {
233 int count = 0;
234 TRACE( ", fType=");
235 MENUFLAG( MFT_SEPARATOR, "sep");
236 MENUFLAG( MFT_OWNERDRAW, "own");
237 MENUFLAG( MFT_BITMAP, "bit");
238 MENUFLAG(MF_POPUP, "pop");
239 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240 MENUFLAG(MFT_MENUBREAK, "brk");
241 MENUFLAG(MFT_RADIOCHECK, "radio");
242 MENUFLAG(MFT_RIGHTORDER, "rorder");
243 MENUFLAG(MF_SYSMENU, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
245 if (flags)
246 TRACE( "+0x%x", flags);
248 flags = mp->fState;
249 if (flags) {
250 int count = 0;
251 TRACE( ", State=");
252 MENUFLAG(MFS_GRAYED, "grey");
253 MENUFLAG(MFS_DEFAULT, "default");
254 MENUFLAG(MFS_DISABLED, "dis");
255 MENUFLAG(MFS_CHECKED, "check");
256 MENUFLAG(MFS_HILITE, "hi");
257 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
258 MENUFLAG(MF_MOUSESELECT, "mouse");
259 if (flags)
260 TRACE( "+0x%x", flags);
262 if (mp->hCheckBit)
263 TRACE( ", Chk=%p", mp->hCheckBit);
264 if (mp->hUnCheckBit)
265 TRACE( ", Unc=%p", mp->hUnCheckBit);
266 if (mp->text)
267 TRACE( ", Text=%s", debugstr_w(mp->text));
268 if (mp->dwItemData)
269 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
270 if (mp->hbmpItem)
272 if( IS_MAGIC_BITMAP(mp->hbmpItem))
273 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
274 else
275 TRACE( ", hbitmap=%p", mp->hbmpItem);
277 TRACE( " }");
278 } else
279 TRACE( "NULL");
280 TRACE(" %s\n", postfix);
283 #undef MENUOUT
284 #undef MENUFLAG
287 /***********************************************************************
288 * MENU_GetMenu
290 * Validate the given menu handle and returns the menu structure pointer.
292 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
294 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
295 if (!menu || menu->wMagic != MENU_MAGIC)
297 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
298 menu = NULL;
300 return menu;
303 /***********************************************************************
304 * get_win_sys_menu
306 * Get the system menu of a window
308 static HMENU get_win_sys_menu( HWND hwnd )
310 HMENU ret = 0;
311 WND *win = WIN_GetPtr( hwnd );
312 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
314 ret = win->hSysMenu;
315 WIN_ReleasePtr( win );
317 return ret;
320 /***********************************************************************
321 * get_menu_font
323 static HFONT get_menu_font( BOOL bold )
325 static HFONT hMenuFont, hMenuFontBold;
327 HFONT ret = bold ? hMenuFontBold : hMenuFont;
329 if (!ret)
331 NONCLIENTMETRICSW ncm;
332 HFONT prev;
334 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
335 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
337 if (bold)
339 ncm.lfMenuFont.lfWeight += 300;
340 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
342 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
343 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
344 ret, NULL );
345 if (prev)
347 /* another thread beat us to it */
348 DeleteObject( ret );
349 ret = prev;
352 return ret;
355 /***********************************************************************
356 * get_arrow_bitmap
358 static HBITMAP get_arrow_bitmap(void)
360 static HBITMAP arrow_bitmap;
362 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
363 return arrow_bitmap;
366 /***********************************************************************
367 * get_down_arrow_bitmap
369 static HBITMAP get_down_arrow_bitmap(void)
371 static HBITMAP arrow_bitmap;
373 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
374 return arrow_bitmap;
377 /***********************************************************************
378 * get_down_arrow_inactive_bitmap
380 static HBITMAP get_down_arrow_inactive_bitmap(void)
382 static HBITMAP arrow_bitmap;
384 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
385 return arrow_bitmap;
388 /***********************************************************************
389 * get_up_arrow_bitmap
391 static HBITMAP get_up_arrow_bitmap(void)
393 static HBITMAP arrow_bitmap;
395 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
396 return arrow_bitmap;
399 /***********************************************************************
400 * get_up_arrow_inactive_bitmap
402 static HBITMAP get_up_arrow_inactive_bitmap(void)
404 static HBITMAP arrow_bitmap;
406 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
407 return arrow_bitmap;
410 /***********************************************************************
411 * MENU_CopySysPopup
413 * Return the default system menu.
415 static HMENU MENU_CopySysPopup(void)
417 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
418 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
420 if( hMenu ) {
421 POPUPMENU* menu = MENU_GetMenu(hMenu);
422 menu->wFlags |= MF_SYSMENU | MF_POPUP;
423 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
425 else
426 ERR("Unable to load default system menu\n" );
428 TRACE("returning %p.\n", hMenu );
430 return hMenu;
434 /**********************************************************************
435 * MENU_GetSysMenu
437 * Create a copy of the system menu. System menu in Windows is
438 * a special menu bar with the single entry - system menu popup.
439 * This popup is presented to the outside world as a "system menu".
440 * However, the real system menu handle is sometimes seen in the
441 * WM_MENUSELECT parameters (and Word 6 likes it this way).
443 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
445 HMENU hMenu;
447 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
448 if ((hMenu = CreateMenu()))
450 POPUPMENU *menu = MENU_GetMenu(hMenu);
451 menu->wFlags = MF_SYSMENU;
452 menu->hWnd = WIN_GetFullHandle( hWnd );
453 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
455 if (!hPopupMenu)
456 hPopupMenu = MENU_CopySysPopup();
458 if (hPopupMenu)
460 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
461 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
463 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
464 (UINT_PTR)hPopupMenu, NULL );
466 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
467 menu->items[0].fState = 0;
468 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
470 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
471 return hMenu;
473 DestroyMenu( hMenu );
475 ERR("failed to load system menu!\n");
476 return 0;
480 /***********************************************************************
481 * MENU_InitSysMenuPopup
483 * Grey the appropriate items in System menu.
485 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
487 BOOL gray;
489 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
490 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
491 gray = ((style & WS_MAXIMIZE) != 0);
492 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
493 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
494 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
496 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
497 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
498 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
499 gray = (clsStyle & CS_NOCLOSE) != 0;
501 /* The menu item must keep its state if it's disabled */
502 if(gray)
503 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
507 /******************************************************************************
509 * UINT MENU_GetStartOfNextColumn(
510 * HMENU hMenu )
512 *****************************************************************************/
514 static UINT MENU_GetStartOfNextColumn(
515 HMENU hMenu )
517 POPUPMENU *menu = MENU_GetMenu(hMenu);
518 UINT i;
520 if(!menu)
521 return NO_SELECTED_ITEM;
523 i = menu->FocusedItem + 1;
524 if( i == NO_SELECTED_ITEM )
525 return i;
527 for( ; i < menu->nItems; ++i ) {
528 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
529 return i;
532 return NO_SELECTED_ITEM;
536 /******************************************************************************
538 * UINT MENU_GetStartOfPrevColumn(
539 * HMENU hMenu )
541 *****************************************************************************/
543 static UINT MENU_GetStartOfPrevColumn(
544 HMENU hMenu )
546 POPUPMENU *menu = MENU_GetMenu(hMenu);
547 UINT i;
549 if( !menu )
550 return NO_SELECTED_ITEM;
552 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
553 return NO_SELECTED_ITEM;
555 /* Find the start of the column */
557 for(i = menu->FocusedItem; i != 0 &&
558 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
559 --i); /* empty */
561 if(i == 0)
562 return NO_SELECTED_ITEM;
564 for(--i; i != 0; --i) {
565 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
566 break;
569 TRACE("ret %d.\n", i );
571 return i;
576 /***********************************************************************
577 * MENU_FindItem
579 * Find a menu item. Return a pointer on the item, and modifies *hmenu
580 * in case the item was in a sub-menu.
582 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
584 POPUPMENU *menu;
585 MENUITEM *fallback = NULL;
586 UINT fallback_pos = 0;
587 UINT i;
589 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
590 if (wFlags & MF_BYPOSITION)
592 if (*nPos >= menu->nItems) return NULL;
593 return &menu->items[*nPos];
595 else
597 MENUITEM *item = menu->items;
598 for (i = 0; i < menu->nItems; i++, item++)
600 if (item->fType & MF_POPUP)
602 HMENU hsubmenu = item->hSubMenu;
603 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
604 if (subitem)
606 *hmenu = hsubmenu;
607 return subitem;
609 else if (item->wID == *nPos)
611 /* fallback to this item if nothing else found */
612 fallback_pos = i;
613 fallback = item;
616 else if (item->wID == *nPos)
618 *nPos = i;
619 return item;
624 if (fallback)
625 *nPos = fallback_pos;
627 return fallback;
630 /***********************************************************************
631 * MENU_FindSubMenu
633 * Find a Sub menu. Return the position of the submenu, and modifies
634 * *hmenu in case it is found in another sub-menu.
635 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
637 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
639 POPUPMENU *menu;
640 UINT i;
641 MENUITEM *item;
642 if (((*hmenu)==(HMENU)0xffff) ||
643 (!(menu = MENU_GetMenu(*hmenu))))
644 return NO_SELECTED_ITEM;
645 item = menu->items;
646 for (i = 0; i < menu->nItems; i++, item++) {
647 if(!(item->fType & MF_POPUP)) continue;
648 if (item->hSubMenu == hSubTarget) {
649 return i;
651 else {
652 HMENU hsubmenu = item->hSubMenu;
653 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
654 if (pos != NO_SELECTED_ITEM) {
655 *hmenu = hsubmenu;
656 return pos;
660 return NO_SELECTED_ITEM;
663 /***********************************************************************
664 * MENU_FreeItemData
666 static void MENU_FreeItemData( MENUITEM* item )
668 /* delete text */
669 HeapFree( GetProcessHeap(), 0, item->text );
672 /***********************************************************************
673 * MENU_AdjustMenuItemRect
675 * Adjust menu item rectangle according to scrolling state.
677 static void
678 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
680 if (menu->bScrolling)
682 UINT arrow_bitmap_height;
683 BITMAP bmp;
685 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
686 arrow_bitmap_height = bmp.bmHeight;
687 rect->top += arrow_bitmap_height - menu->nScrollPos;
688 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
693 /***********************************************************************
694 * MENU_FindItemByCoords
696 * Find the item at the specified coordinates (screen coords). Does
697 * not work for child windows and therefore should not be called for
698 * an arbitrary system menu.
700 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
701 POINT pt, UINT *pos )
703 MENUITEM *item;
704 UINT i;
705 RECT rect;
707 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
708 pt.x -= rect.left;
709 pt.y -= rect.top;
710 item = menu->items;
711 for (i = 0; i < menu->nItems; i++, item++)
713 rect = item->rect;
714 MENU_AdjustMenuItemRect(menu, &rect);
715 if (PtInRect(&rect, pt))
717 if (pos) *pos = i;
718 return item;
721 return NULL;
725 /***********************************************************************
726 * MENU_FindItemByKey
728 * Find the menu item selected by a key press.
729 * Return item id, -1 if none, -2 if we should close the menu.
731 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
732 WCHAR key, BOOL forceMenuChar )
734 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
736 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
738 if (hmenu)
740 POPUPMENU *menu = MENU_GetMenu( hmenu );
741 MENUITEM *item = menu->items;
742 LRESULT menuchar;
744 if( !forceMenuChar )
746 UINT i;
748 for (i = 0; i < menu->nItems; i++, item++)
750 if( item->text)
752 WCHAR *p = item->text - 2;
755 p = strchrW (p + 2, '&');
757 while (p != NULL && p [1] == '&');
758 if (p && (toupperW(p[1]) == toupperW(key))) return i;
762 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
763 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
764 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
765 if (HIWORD(menuchar) == 1) return (UINT)(-2);
767 return (UINT)(-1);
771 /***********************************************************************
772 * MENU_GetBitmapItemSize
774 * Get the size of a bitmap item.
776 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
777 HWND hwndOwner)
779 BITMAP bm;
780 HBITMAP bmp = lpitem->hbmpItem;
782 size->cx = size->cy = 0;
784 /* check if there is a magic menu item associated with this item */
785 switch( (INT_PTR) bmp )
787 case (INT_PTR)HBMMENU_CALLBACK:
789 MEASUREITEMSTRUCT measItem;
790 measItem.CtlType = ODT_MENU;
791 measItem.CtlID = 0;
792 measItem.itemID = lpitem->wID;
793 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
794 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
795 measItem.itemData = lpitem->dwItemData;
796 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
797 size->cx = measItem.itemWidth;
798 size->cy = measItem.itemHeight;
799 return;
801 break;
802 case (INT_PTR)HBMMENU_SYSTEM:
803 if (lpitem->dwItemData)
805 bmp = (HBITMAP)lpitem->dwItemData;
806 break;
808 /* fall through */
809 case (INT_PTR)HBMMENU_MBAR_RESTORE:
810 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
811 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
812 case (INT_PTR)HBMMENU_MBAR_CLOSE:
813 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
814 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
815 size->cy = size->cx;
816 return;
817 case (INT_PTR)HBMMENU_POPUP_CLOSE:
818 case (INT_PTR)HBMMENU_POPUP_RESTORE:
819 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
820 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
821 FIXME("Magic %p not implemented\n", bmp );
822 return;
824 if (GetObjectW(bmp, sizeof(bm), &bm ))
826 size->cx = bm.bmWidth;
827 size->cy = bm.bmHeight;
831 /***********************************************************************
832 * MENU_DrawBitmapItem
834 * Draw a bitmap item.
836 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
837 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
839 BITMAP bm;
840 DWORD rop;
841 HDC hdcMem;
842 HBITMAP bmp;
843 int w = rect->right - rect->left;
844 int h = rect->bottom - rect->top;
845 int bmp_xoffset = 0;
846 int left, top;
847 HBITMAP hbmToDraw = lpitem->hbmpItem;
848 bmp = hbmToDraw;
850 /* Check if there is a magic menu item associated with this item */
851 if (IS_MAGIC_BITMAP(hbmToDraw))
853 UINT flags = 0;
854 RECT r;
856 switch((INT_PTR)hbmToDraw)
858 case (INT_PTR)HBMMENU_SYSTEM:
859 if (lpitem->dwItemData)
861 bmp = (HBITMAP)lpitem->dwItemData;
862 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
864 else
866 static HBITMAP hBmpSysMenu;
868 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
869 bmp = hBmpSysMenu;
870 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
871 /* only use right half of the bitmap */
872 bmp_xoffset = bm.bmWidth / 2;
873 bm.bmWidth -= bmp_xoffset;
875 goto got_bitmap;
876 case (INT_PTR)HBMMENU_MBAR_RESTORE:
877 flags = DFCS_CAPTIONRESTORE;
878 break;
879 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
880 flags = DFCS_CAPTIONMIN;
881 break;
882 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
883 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
884 break;
885 case (INT_PTR)HBMMENU_MBAR_CLOSE:
886 flags = DFCS_CAPTIONCLOSE;
887 break;
888 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
889 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
890 break;
891 case (INT_PTR)HBMMENU_CALLBACK:
893 DRAWITEMSTRUCT drawItem;
894 drawItem.CtlType = ODT_MENU;
895 drawItem.CtlID = 0;
896 drawItem.itemID = lpitem->wID;
897 drawItem.itemAction = odaction;
898 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
899 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
900 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
901 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
902 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
903 drawItem.hwndItem = (HWND)hmenu;
904 drawItem.hDC = hdc;
905 drawItem.itemData = lpitem->dwItemData;
906 drawItem.rcItem = *rect;
907 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
908 return;
910 break;
911 case (INT_PTR)HBMMENU_POPUP_CLOSE:
912 case (INT_PTR)HBMMENU_POPUP_RESTORE:
913 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
914 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
915 default:
916 FIXME("Magic %p not implemented\n", hbmToDraw);
917 return;
919 r = *rect;
920 InflateRect( &r, -1, -1 );
921 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
922 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
923 return;
926 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
928 got_bitmap:
929 hdcMem = CreateCompatibleDC( hdc );
930 SelectObject( hdcMem, bmp );
932 /* handle fontsize > bitmap_height */
933 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
934 left=rect->left;
935 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
936 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
937 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
938 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
939 DeleteDC( hdcMem );
943 /***********************************************************************
944 * MENU_CalcItemSize
946 * Calculate the size of the menu item and store it in lpitem->rect.
948 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
949 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
951 WCHAR *p;
952 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
953 UINT arrow_bitmap_width;
954 BITMAP bm;
955 INT itemheight;
957 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
958 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
959 (menuBar ? " (MenuBar)" : ""));
961 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
962 arrow_bitmap_width = bm.bmWidth;
964 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
965 if( !menucharsize.cx ) {
966 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
967 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
968 * but it is unlikely an application will depend on that */
969 ODitemheight = HIWORD( GetDialogBaseUnits());
972 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
974 if (lpitem->fType & MF_OWNERDRAW)
976 MEASUREITEMSTRUCT mis;
977 mis.CtlType = ODT_MENU;
978 mis.CtlID = 0;
979 mis.itemID = lpitem->wID;
980 mis.itemData = lpitem->dwItemData;
981 mis.itemHeight = ODitemheight;
982 mis.itemWidth = 0;
983 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
984 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
985 * width of a menufont character to the width of an owner-drawn menu.
987 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
988 if (menuBar) {
989 /* under at least win95 you seem to be given a standard
990 height for the menu and the height value is ignored */
991 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
992 } else
993 lpitem->rect.bottom += mis.itemHeight;
995 TRACE("id=%04lx size=%dx%d\n",
996 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
997 lpitem->rect.bottom-lpitem->rect.top);
998 return;
1001 if (lpitem->fType & MF_SEPARATOR)
1003 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1004 if( !menuBar)
1005 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1006 return;
1009 itemheight = 0;
1010 lpitem->xTab = 0;
1012 if (!menuBar) {
1013 if (lpitem->hbmpItem) {
1014 SIZE size;
1016 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1017 /* Keep the size of the bitmap in callback mode to be able
1018 * to draw it correctly */
1019 lpitem->bmpsize = size;
1020 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1021 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1022 lpitem->rect.right += size.cx + 2;
1023 itemheight = size.cy + 2;
1025 if( !(lppop->dwStyle & MNS_NOCHECK))
1026 lpitem->rect.right += check_bitmap_width;
1027 lpitem->rect.right += 4 + menucharsize.cx;
1028 lpitem->xTab = lpitem->rect.right;
1029 lpitem->rect.right += arrow_bitmap_width;
1030 } else if (lpitem->hbmpItem) { /* menuBar */
1031 SIZE size;
1033 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1034 lpitem->bmpsize = size;
1035 lpitem->rect.right += size.cx;
1036 if( lpitem->text) lpitem->rect.right += 2;
1037 itemheight = size.cy;
1040 /* it must be a text item - unless it's the system menu */
1041 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1042 HFONT hfontOld = NULL;
1043 RECT rc = lpitem->rect;
1044 LONG txtheight, txtwidth;
1046 if ( lpitem->fState & MFS_DEFAULT ) {
1047 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1049 if (menuBar) {
1050 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1051 DT_SINGLELINE|DT_CALCRECT);
1052 lpitem->rect.right += rc.right - rc.left;
1053 itemheight = max( max( itemheight, txtheight),
1054 GetSystemMetrics( SM_CYMENU) - 1);
1055 lpitem->rect.right += 2 * menucharsize.cx;
1056 } else {
1057 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1058 RECT tmprc = rc;
1059 LONG tmpheight;
1060 int n = (int)( p - lpitem->text);
1061 /* Item contains a tab (only meaningful in popup menus) */
1062 /* get text size before the tab */
1063 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1064 DT_SINGLELINE|DT_CALCRECT);
1065 txtwidth = rc.right - rc.left;
1066 p += 1; /* advance past the Tab */
1067 /* get text size after the tab */
1068 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1069 DT_SINGLELINE|DT_CALCRECT);
1070 lpitem->xTab += txtwidth;
1071 txtheight = max( txtheight, tmpheight);
1072 txtwidth += menucharsize.cx + /* space for the tab */
1073 tmprc.right - tmprc.left; /* space for the short cut */
1074 } else {
1075 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1076 DT_SINGLELINE|DT_CALCRECT);
1077 txtwidth = rc.right - rc.left;
1078 lpitem->xTab += txtwidth;
1080 lpitem->rect.right += 2 + txtwidth;
1081 itemheight = max( itemheight,
1082 max( txtheight + 2, menucharsize.cy + 4));
1084 if (hfontOld) SelectObject (hdc, hfontOld);
1085 } else if( menuBar) {
1086 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1088 lpitem->rect.bottom += itemheight;
1089 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1093 /***********************************************************************
1094 * MENU_GetMaxPopupHeight
1096 static UINT
1097 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1099 if (lppop->cyMax)
1100 return lppop->cyMax;
1101 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1105 /***********************************************************************
1106 * MENU_PopupMenuCalcSize
1108 * Calculate the size of a popup menu.
1110 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1112 MENUITEM *lpitem;
1113 HDC hdc;
1114 int start, i;
1115 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1117 lppop->Width = lppop->Height = 0;
1118 if (lppop->nItems == 0) return;
1119 hdc = GetDC( 0 );
1121 SelectObject( hdc, get_menu_font(FALSE));
1123 start = 0;
1124 maxX = 2 + 1;
1126 lppop->maxBmpSize.cx = 0;
1127 lppop->maxBmpSize.cy = 0;
1129 while (start < lppop->nItems)
1131 lpitem = &lppop->items[start];
1132 orgX = maxX;
1133 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1134 orgX += MENU_COL_SPACE;
1135 orgY = MENU_TOP_MARGIN;
1137 maxTab = maxTabWidth = 0;
1138 /* Parse items until column break or end of menu */
1139 for (i = start; i < lppop->nItems; i++, lpitem++)
1141 if ((i != start) &&
1142 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1144 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1145 maxX = max( maxX, lpitem->rect.right );
1146 orgY = lpitem->rect.bottom;
1147 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1149 maxTab = max( maxTab, lpitem->xTab );
1150 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1154 /* Finish the column (set all items to the largest width found) */
1155 maxX = max( maxX, maxTab + maxTabWidth );
1156 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1158 lpitem->rect.right = maxX;
1159 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1160 lpitem->xTab = maxTab;
1163 lppop->Height = max( lppop->Height, orgY );
1166 lppop->Width = maxX;
1168 /* space for 3d border */
1169 lppop->Height += MENU_BOTTOM_MARGIN;
1170 lppop->Width += 2;
1172 /* Adjust popup height if it exceeds maximum */
1173 maxHeight = MENU_GetMaxPopupHeight(lppop);
1174 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1175 if (lppop->Height >= maxHeight)
1177 lppop->Height = maxHeight;
1178 lppop->bScrolling = TRUE;
1180 else
1182 lppop->bScrolling = FALSE;
1185 ReleaseDC( 0, hdc );
1189 /***********************************************************************
1190 * MENU_MenuBarCalcSize
1192 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1193 * height is off by 1 pixel which causes lengthy window relocations when
1194 * active document window is maximized/restored.
1196 * Calculate the size of the menu bar.
1198 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1199 LPPOPUPMENU lppop, HWND hwndOwner )
1201 MENUITEM *lpitem;
1202 int start, i, orgX, orgY, maxY, helpPos;
1204 if ((lprect == NULL) || (lppop == NULL)) return;
1205 if (lppop->nItems == 0) return;
1206 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1207 lppop->Width = lprect->right - lprect->left;
1208 lppop->Height = 0;
1209 maxY = lprect->top+1;
1210 start = 0;
1211 helpPos = -1;
1212 lppop->maxBmpSize.cx = 0;
1213 lppop->maxBmpSize.cy = 0;
1214 while (start < lppop->nItems)
1216 lpitem = &lppop->items[start];
1217 orgX = lprect->left;
1218 orgY = maxY;
1220 /* Parse items until line break or end of menu */
1221 for (i = start; i < lppop->nItems; i++, lpitem++)
1223 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1224 if ((i != start) &&
1225 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1227 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1228 debug_print_menuitem (" item: ", lpitem, "");
1229 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1231 if (lpitem->rect.right > lprect->right)
1233 if (i != start) break;
1234 else lpitem->rect.right = lprect->right;
1236 maxY = max( maxY, lpitem->rect.bottom );
1237 orgX = lpitem->rect.right;
1240 /* Finish the line (set all items to the largest height found) */
1241 while (start < i) lppop->items[start++].rect.bottom = maxY;
1244 lprect->bottom = maxY;
1245 lppop->Height = lprect->bottom - lprect->top;
1247 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1248 /* the last item (if several lines, only move the last line) */
1249 lpitem = &lppop->items[lppop->nItems-1];
1250 orgY = lpitem->rect.top;
1251 orgX = lprect->right;
1252 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1253 if ( (helpPos==-1) || (helpPos>i) )
1254 break; /* done */
1255 if (lpitem->rect.top != orgY) break; /* Other line */
1256 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1257 lpitem->rect.left += orgX - lpitem->rect.right;
1258 lpitem->rect.right = orgX;
1259 orgX = lpitem->rect.left;
1264 /***********************************************************************
1265 * MENU_DrawScrollArrows
1267 * Draw scroll arrows.
1269 static void
1270 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1272 HDC hdcMem = CreateCompatibleDC(hdc);
1273 HBITMAP hOrigBitmap;
1274 UINT arrow_bitmap_width, arrow_bitmap_height;
1275 BITMAP bmp;
1276 RECT rect;
1278 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1279 arrow_bitmap_width = bmp.bmWidth;
1280 arrow_bitmap_height = bmp.bmHeight;
1283 if (lppop->nScrollPos)
1284 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1285 else
1286 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1287 rect.left = 0;
1288 rect.top = 0;
1289 rect.right = lppop->Width;
1290 rect.bottom = arrow_bitmap_height;
1291 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1292 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1293 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1294 rect.top = lppop->Height - arrow_bitmap_height;
1295 rect.bottom = lppop->Height;
1296 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1297 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1298 SelectObject(hdcMem, get_down_arrow_bitmap());
1299 else
1300 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1301 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1302 lppop->Height - arrow_bitmap_height,
1303 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1304 SelectObject(hdcMem, hOrigBitmap);
1305 DeleteDC(hdcMem);
1309 /***********************************************************************
1310 * draw_popup_arrow
1312 * Draws the popup-menu arrow.
1314 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1315 UINT arrow_bitmap_height)
1317 HDC hdcMem = CreateCompatibleDC( hdc );
1318 HBITMAP hOrigBitmap;
1320 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1321 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1322 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1323 arrow_bitmap_width, arrow_bitmap_height,
1324 hdcMem, 0, 0, SRCCOPY );
1325 SelectObject( hdcMem, hOrigBitmap );
1326 DeleteDC( hdcMem );
1328 /***********************************************************************
1329 * MENU_DrawMenuItem
1331 * Draw a single menu item.
1333 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1334 UINT height, BOOL menuBar, UINT odaction )
1336 RECT rect;
1337 BOOL flat_menu = FALSE;
1338 int bkgnd;
1339 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1340 POPUPMENU *menu = MENU_GetMenu(hmenu);
1341 RECT bmprc;
1343 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1345 if (!menuBar) {
1346 BITMAP bmp;
1347 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1348 arrow_bitmap_width = bmp.bmWidth;
1349 arrow_bitmap_height = bmp.bmHeight;
1352 if (lpitem->fType & MF_SYSMENU)
1354 if( !IsIconic(hwnd) )
1355 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1356 return;
1359 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1360 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1362 /* Setup colors */
1364 if (lpitem->fState & MF_HILITE)
1366 if(menuBar && !flat_menu) {
1367 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1368 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1369 } else {
1370 if(lpitem->fState & MF_GRAYED)
1371 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1372 else
1373 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1374 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1377 else
1379 if (lpitem->fState & MF_GRAYED)
1380 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1381 else
1382 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1383 SetBkColor( hdc, GetSysColor( bkgnd ) );
1386 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1387 rect = lpitem->rect;
1388 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1390 if (lpitem->fType & MF_OWNERDRAW)
1393 ** Experimentation under Windows reveals that an owner-drawn
1394 ** menu is given the rectangle which includes the space it requested
1395 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1396 ** and a popup-menu arrow. This is the value of lpitem->rect.
1397 ** Windows will leave all drawing to the application except for
1398 ** the popup-menu arrow. Windows always draws that itself, after
1399 ** the menu owner has finished drawing.
1401 DRAWITEMSTRUCT dis;
1403 dis.CtlType = ODT_MENU;
1404 dis.CtlID = 0;
1405 dis.itemID = lpitem->wID;
1406 dis.itemData = lpitem->dwItemData;
1407 dis.itemState = 0;
1408 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1409 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1410 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1411 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1412 dis.hwndItem = (HWND)hmenu;
1413 dis.hDC = hdc;
1414 dis.rcItem = rect;
1415 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1416 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1417 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1418 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1419 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1420 /* Draw the popup-menu arrow */
1421 if (lpitem->fType & MF_POPUP)
1422 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1423 arrow_bitmap_height);
1424 return;
1427 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1429 if (lpitem->fState & MF_HILITE)
1431 if (flat_menu)
1433 InflateRect (&rect, -1, -1);
1434 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1435 InflateRect (&rect, 1, 1);
1436 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1438 else
1440 if(menuBar)
1441 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1442 else
1443 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1446 else
1447 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1449 SetBkMode( hdc, TRANSPARENT );
1451 /* vertical separator */
1452 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1454 HPEN oldPen;
1455 RECT rc = rect;
1457 rc.left -= MENU_COL_SPACE / 2 + 1;
1458 rc.top = 3;
1459 rc.bottom = height - 3;
1460 if (flat_menu)
1462 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1463 MoveToEx( hdc, rc.left, rc.top, NULL );
1464 LineTo( hdc, rc.left, rc.bottom );
1465 SelectObject( hdc, oldPen );
1467 else
1468 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1471 /* horizontal separator */
1472 if (lpitem->fType & MF_SEPARATOR)
1474 HPEN oldPen;
1475 RECT rc = rect;
1477 rc.left++;
1478 rc.right--;
1479 rc.top = ( rc.top + rc.bottom) / 2;
1480 if (flat_menu)
1482 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1483 MoveToEx( hdc, rc.left, rc.top, NULL );
1484 LineTo( hdc, rc.right, rc.top );
1485 SelectObject( hdc, oldPen );
1487 else
1488 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1489 return;
1492 /* helper lines for debugging */
1493 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1494 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1495 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1496 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1499 if (lpitem->hbmpItem) {
1500 /* calculate the bitmap rectangle in coordinates relative
1501 * to the item rectangle */
1502 if( menuBar) {
1503 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1504 bmprc.left = 3;
1505 else
1506 bmprc.left = lpitem->text ? menucharsize.cx : 0;
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, UINT flags,
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 );
1807 if( flags & TPM_RIGHTALIGN ) x -= width;
1808 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1810 if( flags & TPM_BOTTOMALIGN ) y -= height;
1811 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1813 if( x + width > info.rcWork.right)
1815 if( xanchor && x >= width - xanchor )
1816 x -= width - xanchor;
1818 if( x + width > info.rcWork.right)
1819 x = info.rcWork.right - width;
1821 if( x < info.rcWork.left ) x = info.rcWork.left;
1823 if( y + height > info.rcWork.bottom)
1825 if( yanchor && y >= height + yanchor )
1826 y -= height + yanchor;
1828 if( y + height > info.rcWork.bottom)
1829 y = info.rcWork.bottom - height;
1831 if( y < info.rcWork.top ) y = info.rcWork.top;
1833 /* NOTE: In Windows, top menu popup is not owned. */
1834 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1835 WS_POPUP, x, y, width, height,
1836 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1837 (LPVOID)hmenu );
1838 if( !menu->hWnd ) return FALSE;
1839 if (!top_popup) top_popup = menu->hWnd;
1841 /* Display the window */
1843 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1844 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1845 UpdateWindow( menu->hWnd );
1846 return TRUE;
1850 /***********************************************************************
1851 * MENU_EnsureMenuItemVisible
1853 static void
1854 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1856 if (lppop->bScrolling)
1858 MENUITEM *item = &lppop->items[wIndex];
1859 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1860 UINT nOldPos = lppop->nScrollPos;
1861 RECT rc;
1862 UINT arrow_bitmap_height;
1863 BITMAP bmp;
1865 GetClientRect(lppop->hWnd, &rc);
1867 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1868 arrow_bitmap_height = bmp.bmHeight;
1870 rc.top += arrow_bitmap_height;
1871 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1873 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1874 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1877 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1878 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1879 MENU_DrawScrollArrows(lppop, hdc);
1881 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1883 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1884 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1885 MENU_DrawScrollArrows(lppop, hdc);
1891 /***********************************************************************
1892 * MENU_SelectItem
1894 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1895 BOOL sendMenuSelect, HMENU topmenu )
1897 LPPOPUPMENU lppop;
1898 HDC hdc;
1900 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1902 lppop = MENU_GetMenu( hmenu );
1903 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1905 if (lppop->FocusedItem == wIndex) return;
1906 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1907 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1908 if (!top_popup) top_popup = lppop->hWnd;
1910 SelectObject( hdc, get_menu_font(FALSE));
1912 /* Clear previous highlighted item */
1913 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1915 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1916 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1917 lppop->Height, !(lppop->wFlags & MF_POPUP),
1918 ODA_SELECT );
1921 /* Highlight new item (if any) */
1922 lppop->FocusedItem = wIndex;
1923 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1925 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1926 lppop->items[wIndex].fState |= MF_HILITE;
1927 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1928 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1929 &lppop->items[wIndex], lppop->Height,
1930 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1932 if (sendMenuSelect)
1934 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1935 SendMessageW( hwndOwner, WM_MENUSELECT,
1936 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1937 ip->fType | ip->fState |
1938 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1941 else if (sendMenuSelect) {
1942 if(topmenu){
1943 int pos;
1944 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1945 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1946 MENUITEM *ip = &ptm->items[pos];
1947 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1948 ip->fType | ip->fState |
1949 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1953 ReleaseDC( lppop->hWnd, hdc );
1957 /***********************************************************************
1958 * MENU_MoveSelection
1960 * Moves currently selected item according to the offset parameter.
1961 * If there is no selection then it should select the last item if
1962 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1964 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1966 INT i;
1967 POPUPMENU *menu;
1969 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1971 menu = MENU_GetMenu( hmenu );
1972 if ((!menu) || (!menu->items)) return;
1974 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1976 if( menu->nItems == 1 ) return; else
1977 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1978 ; i += offset)
1979 if (!(menu->items[i].fType & MF_SEPARATOR))
1981 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1982 return;
1986 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1987 i >= 0 && i < menu->nItems ; i += offset)
1988 if (!(menu->items[i].fType & MF_SEPARATOR))
1990 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1991 return;
1996 /**********************************************************************
1997 * MENU_SetItemData
1999 * Set an item's flags, id and text ptr. Called by InsertMenu() and
2000 * ModifyMenu().
2002 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
2003 LPCWSTR str )
2005 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2006 TRACE("flags=%x str=%p\n", flags, str);
2008 if (IS_STRING_ITEM(flags))
2010 LPWSTR prevText = item->text;
2011 if (!str)
2013 flags |= MF_SEPARATOR;
2014 item->text = NULL;
2016 else
2018 LPWSTR text;
2019 /* Item beginning with a backspace is a help item */
2020 if (*str == '\b')
2022 flags |= MF_HELP;
2023 str++;
2025 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2026 return FALSE;
2027 strcpyW( text, str );
2028 item->text = text;
2030 item->hbmpItem = NULL;
2031 HeapFree( GetProcessHeap(), 0, prevText );
2033 else if(( flags & MFT_BITMAP)) {
2034 item->hbmpItem = HBITMAP_32(LOWORD(str));
2035 /* setting bitmap clears text */
2036 HeapFree( GetProcessHeap(), 0, item->text );
2037 item->text = NULL;
2040 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2042 if (flags & MF_OWNERDRAW)
2043 item->dwItemData = (DWORD_PTR)str;
2044 else
2045 item->dwItemData = 0;
2047 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2048 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2050 if (flags & MF_POPUP)
2052 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2053 if (menu) menu->wFlags |= MF_POPUP;
2054 else
2056 item->wID = 0;
2057 item->hSubMenu = 0;
2058 item->fType = 0;
2059 item->fState = 0;
2060 return FALSE;
2064 item->wID = id;
2065 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2067 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2068 flags |= MF_POPUP; /* keep popup */
2070 item->fType = flags & TYPE_MASK;
2071 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2072 for ModifyMenu, but Windows accepts it */
2073 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2075 /* Don't call SetRectEmpty here! */
2077 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2078 return TRUE;
2082 /**********************************************************************
2083 * MENU_InsertItem
2085 * Insert (allocate) a new item into a menu.
2087 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2089 MENUITEM *newItems;
2090 POPUPMENU *menu;
2092 if (!(menu = MENU_GetMenu(hMenu)))
2093 return NULL;
2095 /* Find where to insert new item */
2097 if (flags & MF_BYPOSITION) {
2098 if (pos > menu->nItems)
2099 pos = menu->nItems;
2100 } else {
2101 if (!MENU_FindItem( &hMenu, &pos, flags ))
2102 pos = menu->nItems;
2103 else {
2104 if (!(menu = MENU_GetMenu( hMenu )))
2105 return NULL;
2109 /* Make sure that MDI system buttons stay on the right side.
2110 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2111 * regardless of their id.
2113 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2114 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2115 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2116 pos--;
2118 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2120 /* Create new items array */
2122 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2123 if (!newItems)
2125 WARN("allocation failed\n" );
2126 return NULL;
2128 if (menu->nItems > 0)
2130 /* Copy the old array into the new one */
2131 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2132 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2133 (menu->nItems-pos)*sizeof(MENUITEM) );
2134 HeapFree( GetProcessHeap(), 0, menu->items );
2136 menu->items = newItems;
2137 menu->nItems++;
2138 memset( &newItems[pos], 0, sizeof(*newItems) );
2139 menu->Height = 0; /* force size recalculate */
2140 return &newItems[pos];
2144 /**********************************************************************
2145 * MENU_ParseResource
2147 * Parse a standard menu resource and add items to the menu.
2148 * Return a pointer to the end of the resource.
2150 * NOTE: flags is equivalent to the mtOption field
2152 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2154 WORD flags, id = 0;
2155 LPCSTR str;
2156 BOOL end_flag;
2160 flags = GET_WORD(res);
2161 end_flag = flags & MF_END;
2162 /* Remove MF_END because it has the same value as MF_HILITE */
2163 flags &= ~MF_END;
2164 res += sizeof(WORD);
2165 if (!(flags & MF_POPUP))
2167 id = GET_WORD(res);
2168 res += sizeof(WORD);
2170 str = res;
2171 if (!unicode) res += strlen(str) + 1;
2172 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2173 if (flags & MF_POPUP)
2175 HMENU hSubMenu = CreatePopupMenu();
2176 if (!hSubMenu) return NULL;
2177 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2178 return NULL;
2179 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2180 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2182 else /* Not a popup */
2184 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2185 else AppendMenuW( hMenu, flags, id,
2186 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2188 } while (!end_flag);
2189 return res;
2193 /**********************************************************************
2194 * MENUEX_ParseResource
2196 * Parse an extended menu resource and add items to the menu.
2197 * Return a pointer to the end of the resource.
2199 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2201 WORD resinfo;
2202 do {
2203 MENUITEMINFOW mii;
2205 mii.cbSize = sizeof(mii);
2206 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2207 mii.fType = GET_DWORD(res);
2208 res += sizeof(DWORD);
2209 mii.fState = GET_DWORD(res);
2210 res += sizeof(DWORD);
2211 mii.wID = GET_DWORD(res);
2212 res += sizeof(DWORD);
2213 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2214 res += sizeof(WORD);
2215 /* Align the text on a word boundary. */
2216 res += (~((UINT_PTR)res - 1)) & 1;
2217 mii.dwTypeData = (LPWSTR) res;
2218 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2219 /* Align the following fields on a dword boundary. */
2220 res += (~((UINT_PTR)res - 1)) & 3;
2222 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2223 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2225 if (resinfo & 1) { /* Pop-up? */
2226 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2227 res += sizeof(DWORD);
2228 mii.hSubMenu = CreatePopupMenu();
2229 if (!mii.hSubMenu)
2230 return NULL;
2231 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2232 DestroyMenu(mii.hSubMenu);
2233 return NULL;
2235 mii.fMask |= MIIM_SUBMENU;
2236 mii.fType |= MF_POPUP;
2238 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2240 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2241 mii.wID, mii.fType);
2242 mii.fType |= MF_SEPARATOR;
2244 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2245 } while (!(resinfo & MF_END));
2246 return res;
2250 /***********************************************************************
2251 * MENU_GetSubPopup
2253 * Return the handle of the selected sub-popup menu (if any).
2255 static HMENU MENU_GetSubPopup( HMENU hmenu )
2257 POPUPMENU *menu;
2258 MENUITEM *item;
2260 menu = MENU_GetMenu( hmenu );
2262 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2264 item = &menu->items[menu->FocusedItem];
2265 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2266 return item->hSubMenu;
2267 return 0;
2271 /***********************************************************************
2272 * MENU_HideSubPopups
2274 * Hide the sub-popup menus of this menu.
2276 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2277 BOOL sendMenuSelect, UINT wFlags )
2279 POPUPMENU *menu = MENU_GetMenu( hmenu );
2281 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2283 if (menu && top_popup)
2285 HMENU hsubmenu;
2286 POPUPMENU *submenu;
2287 MENUITEM *item;
2289 if (menu->FocusedItem != NO_SELECTED_ITEM)
2291 item = &menu->items[menu->FocusedItem];
2292 if (!(item->fType & MF_POPUP) ||
2293 !(item->fState & MF_MOUSESELECT)) return;
2294 item->fState &= ~MF_MOUSESELECT;
2295 hsubmenu = item->hSubMenu;
2296 } else return;
2298 submenu = MENU_GetMenu( hsubmenu );
2299 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2300 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2301 DestroyWindow( submenu->hWnd );
2302 submenu->hWnd = 0;
2304 if (!(wFlags & TPM_NONOTIFY))
2305 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2306 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2311 /***********************************************************************
2312 * MENU_ShowSubPopup
2314 * Display the sub-menu of the selected item of this menu.
2315 * Return the handle of the submenu, or hmenu if no submenu to display.
2317 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2318 BOOL selectFirst, UINT wFlags )
2320 RECT rect;
2321 POPUPMENU *menu;
2322 MENUITEM *item;
2323 HDC hdc;
2325 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2327 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2329 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2331 item = &menu->items[menu->FocusedItem];
2332 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2333 return hmenu;
2335 /* message must be sent before using item,
2336 because nearly everything may be changed by the application ! */
2338 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2339 if (!(wFlags & TPM_NONOTIFY))
2340 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2341 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2343 item = &menu->items[menu->FocusedItem];
2344 rect = item->rect;
2346 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2347 if (!(item->fState & MF_HILITE))
2349 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2350 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2352 SelectObject( hdc, get_menu_font(FALSE));
2354 item->fState |= MF_HILITE;
2355 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2356 ReleaseDC( menu->hWnd, hdc );
2358 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2359 item->rect = rect;
2361 item->fState |= MF_MOUSESELECT;
2363 if (IS_SYSTEM_MENU(menu))
2365 MENU_InitSysMenuPopup(item->hSubMenu,
2366 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2367 GetClassLongW( menu->hWnd, GCL_STYLE));
2369 NC_GetSysPopupPos( menu->hWnd, &rect );
2370 rect.top = rect.bottom;
2371 rect.right = GetSystemMetrics(SM_CXSIZE);
2372 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2374 else
2376 GetWindowRect( menu->hWnd, &rect );
2377 if (menu->wFlags & MF_POPUP)
2379 RECT rc = item->rect;
2381 MENU_AdjustMenuItemRect(menu, &rc);
2383 /* The first item in the popup menu has to be at the
2384 same y position as the focused menu item */
2385 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2386 rect.top += rc.top - MENU_TOP_MARGIN;
2387 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2388 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2389 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2391 else
2393 rect.left += item->rect.left;
2394 rect.top += item->rect.bottom;
2395 rect.right = item->rect.right - item->rect.left;
2396 rect.bottom = item->rect.bottom - item->rect.top;
2400 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2401 rect.left, rect.top, rect.right, rect.bottom );
2402 if (selectFirst)
2403 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2404 return item->hSubMenu;
2409 /**********************************************************************
2410 * MENU_IsMenuActive
2412 HWND MENU_IsMenuActive(void)
2414 return top_popup;
2417 /***********************************************************************
2418 * MENU_PtMenu
2420 * Walks menu chain trying to find a menu pt maps to.
2422 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2424 POPUPMENU *menu = MENU_GetMenu( hMenu );
2425 UINT item = menu->FocusedItem;
2426 HMENU ret;
2428 /* try subpopup first (if any) */
2429 ret = (item != NO_SELECTED_ITEM &&
2430 (menu->items[item].fType & MF_POPUP) &&
2431 (menu->items[item].fState & MF_MOUSESELECT))
2432 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2434 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2436 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2437 if( menu->wFlags & MF_POPUP )
2439 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2441 else if (ht == HTSYSMENU)
2442 ret = get_win_sys_menu( menu->hWnd );
2443 else if (ht == HTMENU)
2444 ret = GetMenu( menu->hWnd );
2446 return ret;
2449 /***********************************************************************
2450 * MENU_ExecFocusedItem
2452 * Execute a menu item (for instance when user pressed Enter).
2453 * Return the wID of the executed item. Otherwise, -1 indicating
2454 * that no menu item was executed, -2 if a popup is shown;
2455 * Have to receive the flags for the TrackPopupMenu options to avoid
2456 * sending unwanted message.
2459 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2461 MENUITEM *item;
2462 POPUPMENU *menu = MENU_GetMenu( hMenu );
2464 TRACE("%p hmenu=%p\n", pmt, hMenu);
2466 if (!menu || !menu->nItems ||
2467 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2469 item = &menu->items[menu->FocusedItem];
2471 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2473 if (!(item->fType & MF_POPUP))
2475 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2477 /* If TPM_RETURNCMD is set you return the id, but
2478 do not send a message to the owner */
2479 if(!(wFlags & TPM_RETURNCMD))
2481 if( menu->wFlags & MF_SYSMENU )
2482 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2483 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2484 else
2486 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2487 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2489 if (dwStyle & MNS_NOTIFYBYPOS)
2490 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2491 (LPARAM)hMenu);
2492 else
2493 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2496 return item->wID;
2499 else
2501 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2502 return -2;
2505 return -1;
2508 /***********************************************************************
2509 * MENU_SwitchTracking
2511 * Helper function for menu navigation routines.
2513 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2515 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2516 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2518 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2520 if( pmt->hTopMenu != hPtMenu &&
2521 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2523 /* both are top level menus (system and menu-bar) */
2524 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2525 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2526 pmt->hTopMenu = hPtMenu;
2528 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2529 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2533 /***********************************************************************
2534 * MENU_ButtonDown
2536 * Return TRUE if we can go on with menu tracking.
2538 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2540 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2542 if (hPtMenu)
2544 UINT id = 0;
2545 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2546 MENUITEM *item;
2548 if( IS_SYSTEM_MENU(ptmenu) )
2549 item = ptmenu->items;
2550 else
2551 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2553 if( item )
2555 if( ptmenu->FocusedItem != id )
2556 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2558 /* If the popup menu is not already "popped" */
2559 if(!(item->fState & MF_MOUSESELECT ))
2561 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2564 return TRUE;
2566 /* Else the click was on the menu bar, finish the tracking */
2568 return FALSE;
2571 /***********************************************************************
2572 * MENU_ButtonUp
2574 * Return the value of MENU_ExecFocusedItem if
2575 * the selected item was not a popup. Else open the popup.
2576 * A -1 return value indicates that we go on with menu tracking.
2579 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2581 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2583 if (hPtMenu)
2585 UINT id = 0;
2586 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2587 MENUITEM *item;
2589 if( IS_SYSTEM_MENU(ptmenu) )
2590 item = ptmenu->items;
2591 else
2592 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2594 if( item && (ptmenu->FocusedItem == id ))
2596 debug_print_menuitem ("FocusedItem: ", item, "");
2598 if( !(item->fType & MF_POPUP) )
2600 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2601 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2602 return executedMenuId;
2605 /* If we are dealing with the top-level menu */
2606 /* and this is a click on an already "popped" item: */
2607 /* Stop the menu tracking and close the opened submenus */
2608 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2609 return 0;
2611 ptmenu->bTimeToHide = TRUE;
2613 return -1;
2617 /***********************************************************************
2618 * MENU_MouseMove
2620 * Return TRUE if we can go on with menu tracking.
2622 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2624 UINT id = NO_SELECTED_ITEM;
2625 POPUPMENU *ptmenu = NULL;
2627 if( hPtMenu )
2629 ptmenu = MENU_GetMenu( hPtMenu );
2630 if( IS_SYSTEM_MENU(ptmenu) )
2631 id = 0;
2632 else
2633 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2636 if( id == NO_SELECTED_ITEM )
2638 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2639 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2642 else if( ptmenu->FocusedItem != id )
2644 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2645 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2647 return TRUE;
2651 /***********************************************************************
2652 * MENU_DoNextMenu
2654 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2656 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2658 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2659 BOOL atEnd = FALSE;
2661 /* When skipping left, we need to do something special after the
2662 first menu. */
2663 if (vk == VK_LEFT && menu->FocusedItem == 0)
2665 atEnd = TRUE;
2667 /* When skipping right, for the non-system menu, we need to
2668 handle the last non-special menu item (ie skip any window
2669 icons such as MDI maximize, restore or close) */
2670 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2672 int i = menu->FocusedItem + 1;
2673 while (i < menu->nItems) {
2674 if ((menu->items[i].wID >= SC_SIZE &&
2675 menu->items[i].wID <= SC_RESTORE)) {
2676 i++;
2677 } else break;
2679 if (i == menu->nItems) {
2680 atEnd = TRUE;
2683 /* When skipping right, we need to cater for the system menu */
2684 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2686 if (menu->FocusedItem == (menu->nItems - 1)) {
2687 atEnd = TRUE;
2691 if( atEnd )
2693 MDINEXTMENU next_menu;
2694 HMENU hNewMenu;
2695 HWND hNewWnd;
2696 UINT id = 0;
2698 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2699 next_menu.hmenuNext = 0;
2700 next_menu.hwndNext = 0;
2701 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2703 TRACE("%p [%p] -> %p [%p]\n",
2704 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2706 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2708 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2709 hNewWnd = pmt->hOwnerWnd;
2710 if( IS_SYSTEM_MENU(menu) )
2712 /* switch to the menu bar */
2714 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2716 if( vk == VK_LEFT )
2718 menu = MENU_GetMenu( hNewMenu );
2719 id = menu->nItems - 1;
2721 /* Skip backwards over any system predefined icons,
2722 eg. MDI close, restore etc icons */
2723 while ((id > 0) &&
2724 (menu->items[id].wID >= SC_SIZE &&
2725 menu->items[id].wID <= SC_RESTORE)) id--;
2728 else if (style & WS_SYSMENU )
2730 /* switch to the system menu */
2731 hNewMenu = get_win_sys_menu( hNewWnd );
2733 else return FALSE;
2735 else /* application returned a new menu to switch to */
2737 hNewMenu = next_menu.hmenuNext;
2738 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2740 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2742 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2744 if (style & WS_SYSMENU &&
2745 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2747 /* get the real system menu */
2748 hNewMenu = get_win_sys_menu(hNewWnd);
2750 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2752 /* FIXME: Not sure what to do here;
2753 * perhaps try to track hNewMenu as a popup? */
2755 TRACE(" -- got confused.\n");
2756 return FALSE;
2759 else return FALSE;
2762 if( hNewMenu != pmt->hTopMenu )
2764 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2765 FALSE, 0 );
2766 if( pmt->hCurrentMenu != pmt->hTopMenu )
2767 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2770 if( hNewWnd != pmt->hOwnerWnd )
2772 pmt->hOwnerWnd = hNewWnd;
2773 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2776 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2777 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2779 return TRUE;
2781 return FALSE;
2784 /***********************************************************************
2785 * MENU_SuspendPopup
2787 * The idea is not to show the popup if the next input message is
2788 * going to hide it anyway.
2790 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2792 MSG msg;
2794 msg.hwnd = pmt->hOwnerWnd;
2796 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2797 pmt->trackFlags |= TF_SKIPREMOVE;
2799 switch( uMsg )
2801 case WM_KEYDOWN:
2802 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2803 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2805 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2806 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2807 if( msg.message == WM_KEYDOWN &&
2808 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2810 pmt->trackFlags |= TF_SUSPENDPOPUP;
2811 return TRUE;
2814 break;
2817 /* failures go through this */
2818 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2819 return FALSE;
2822 /***********************************************************************
2823 * MENU_KeyEscape
2825 * Handle a VK_ESCAPE key event in a menu.
2827 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2829 BOOL bEndMenu = TRUE;
2831 if (pmt->hCurrentMenu != pmt->hTopMenu)
2833 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2835 if (menu->wFlags & MF_POPUP)
2837 HMENU hmenutmp, hmenuprev;
2839 hmenuprev = hmenutmp = pmt->hTopMenu;
2841 /* close topmost popup */
2842 while (hmenutmp != pmt->hCurrentMenu)
2844 hmenuprev = hmenutmp;
2845 hmenutmp = MENU_GetSubPopup( hmenuprev );
2848 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2849 pmt->hCurrentMenu = hmenuprev;
2850 bEndMenu = FALSE;
2854 return bEndMenu;
2857 /***********************************************************************
2858 * MENU_KeyLeft
2860 * Handle a VK_LEFT key event in a menu.
2862 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2864 POPUPMENU *menu;
2865 HMENU hmenutmp, hmenuprev;
2866 UINT prevcol;
2868 hmenuprev = hmenutmp = pmt->hTopMenu;
2869 menu = MENU_GetMenu( hmenutmp );
2871 /* Try to move 1 column left (if possible) */
2872 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2873 NO_SELECTED_ITEM ) {
2875 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2876 prevcol, TRUE, 0 );
2877 return;
2880 /* close topmost popup */
2881 while (hmenutmp != pmt->hCurrentMenu)
2883 hmenuprev = hmenutmp;
2884 hmenutmp = MENU_GetSubPopup( hmenuprev );
2887 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2888 pmt->hCurrentMenu = hmenuprev;
2890 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2892 /* move menu bar selection if no more popups are left */
2894 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2895 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2897 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2899 /* A sublevel menu was displayed - display the next one
2900 * unless there is another displacement coming up */
2902 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2903 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2904 pmt->hTopMenu, TRUE, wFlags);
2910 /***********************************************************************
2911 * MENU_KeyRight
2913 * Handle a VK_RIGHT key event in a menu.
2915 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2917 HMENU hmenutmp;
2918 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2919 UINT nextcol;
2921 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2922 pmt->hCurrentMenu,
2923 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2924 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2926 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2928 /* If already displaying a popup, try to display sub-popup */
2930 hmenutmp = pmt->hCurrentMenu;
2931 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2933 /* if subpopup was displayed then we are done */
2934 if (hmenutmp != pmt->hCurrentMenu) return;
2937 /* Check to see if there's another column */
2938 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2939 NO_SELECTED_ITEM ) {
2940 TRACE("Going to %d.\n", nextcol );
2941 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2942 nextcol, TRUE, 0 );
2943 return;
2946 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2948 if( pmt->hCurrentMenu != pmt->hTopMenu )
2950 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2951 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2952 } else hmenutmp = 0;
2954 /* try to move to the next item */
2955 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2956 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2958 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2959 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2960 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2961 pmt->hTopMenu, TRUE, wFlags);
2965 /***********************************************************************
2966 * MENU_TrackMenu
2968 * Menu tracking code.
2970 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2971 HWND hwnd, const RECT *lprect )
2973 MSG msg;
2974 POPUPMENU *menu;
2975 BOOL fRemove;
2976 INT executedMenuId = -1;
2977 MTRACKER mt;
2978 BOOL enterIdleSent = FALSE;
2979 HWND capture_win;
2981 mt.trackFlags = 0;
2982 mt.hCurrentMenu = hmenu;
2983 mt.hTopMenu = hmenu;
2984 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2985 mt.pt.x = x;
2986 mt.pt.y = y;
2988 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2989 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2991 fEndMenu = FALSE;
2992 if (!(menu = MENU_GetMenu( hmenu )))
2994 WARN("Invalid menu handle %p\n", hmenu);
2995 SetLastError(ERROR_INVALID_MENU_HANDLE);
2996 return FALSE;
2999 if (wFlags & TPM_BUTTONDOWN)
3001 /* Get the result in order to start the tracking or not */
3002 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3003 fEndMenu = !fRemove;
3006 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3008 /* owner may not be visible when tracking a popup, so use the menu itself */
3009 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3010 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3012 while (!fEndMenu)
3014 menu = MENU_GetMenu( mt.hCurrentMenu );
3015 if (!menu) /* sometimes happens if I do a window manager close */
3016 break;
3018 /* we have to keep the message in the queue until it's
3019 * clear that menu loop is not over yet. */
3021 for (;;)
3023 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3025 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3026 /* remove the message from the queue */
3027 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3029 else
3031 if (!enterIdleSent)
3033 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3034 enterIdleSent = TRUE;
3035 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3037 WaitMessage();
3041 /* check if EndMenu() tried to cancel us, by posting this message */
3042 if(msg.message == WM_CANCELMODE)
3044 /* we are now out of the loop */
3045 fEndMenu = TRUE;
3047 /* remove the message from the queue */
3048 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3050 /* break out of internal loop, ala ESCAPE */
3051 break;
3054 TranslateMessage( &msg );
3055 mt.pt = msg.pt;
3057 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3058 enterIdleSent=FALSE;
3060 fRemove = FALSE;
3061 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3064 * Use the mouse coordinates in lParam instead of those in the MSG
3065 * struct to properly handle synthetic messages. They are already
3066 * in screen coordinates.
3068 mt.pt.x = (short)LOWORD(msg.lParam);
3069 mt.pt.y = (short)HIWORD(msg.lParam);
3071 /* Find a menu for this mouse event */
3072 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3074 switch(msg.message)
3076 /* no WM_NC... messages in captured state */
3078 case WM_RBUTTONDBLCLK:
3079 case WM_RBUTTONDOWN:
3080 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3081 /* fall through */
3082 case WM_LBUTTONDBLCLK:
3083 case WM_LBUTTONDOWN:
3084 /* If the message belongs to the menu, removes it from the queue */
3085 /* Else, end menu tracking */
3086 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3087 fEndMenu = !fRemove;
3088 break;
3090 case WM_RBUTTONUP:
3091 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3092 /* fall through */
3093 case WM_LBUTTONUP:
3094 /* Check if a menu was selected by the mouse */
3095 if (hmenu)
3097 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3098 TRACE("executedMenuId %d\n", executedMenuId);
3100 /* End the loop if executedMenuId is an item ID */
3101 /* or if the job was done (executedMenuId = 0). */
3102 fEndMenu = fRemove = (executedMenuId != -1);
3104 /* No menu was selected by the mouse */
3105 /* if the function was called by TrackPopupMenu, continue
3106 with the menu tracking. If not, stop it */
3107 else
3108 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3110 break;
3112 case WM_MOUSEMOVE:
3113 /* the selected menu item must be changed every time */
3114 /* the mouse moves. */
3116 if (hmenu)
3117 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3119 } /* switch(msg.message) - mouse */
3121 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3123 fRemove = TRUE; /* Keyboard messages are always removed */
3124 switch(msg.message)
3126 case WM_KEYDOWN:
3127 case WM_SYSKEYDOWN:
3128 switch(msg.wParam)
3130 case VK_MENU:
3131 fEndMenu = TRUE;
3132 break;
3134 case VK_HOME:
3135 case VK_END:
3136 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3137 NO_SELECTED_ITEM, FALSE, 0 );
3138 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3139 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3140 break;
3142 case VK_UP:
3143 case VK_DOWN: /* If on menu bar, pull-down the menu */
3145 menu = MENU_GetMenu( mt.hCurrentMenu );
3146 if (!(menu->wFlags & MF_POPUP))
3147 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3148 else /* otherwise try to move selection */
3149 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3150 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3151 break;
3153 case VK_LEFT:
3154 MENU_KeyLeft( &mt, wFlags );
3155 break;
3157 case VK_RIGHT:
3158 MENU_KeyRight( &mt, wFlags );
3159 break;
3161 case VK_ESCAPE:
3162 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3163 break;
3165 case VK_F1:
3167 HELPINFO hi;
3168 hi.cbSize = sizeof(HELPINFO);
3169 hi.iContextType = HELPINFO_MENUITEM;
3170 if (menu->FocusedItem == NO_SELECTED_ITEM)
3171 hi.iCtrlId = 0;
3172 else
3173 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3174 hi.hItemHandle = hmenu;
3175 hi.dwContextId = menu->dwContextHelpID;
3176 hi.MousePos = msg.pt;
3177 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3178 break;
3181 default:
3182 break;
3184 break; /* WM_KEYDOWN */
3186 case WM_CHAR:
3187 case WM_SYSCHAR:
3189 UINT pos;
3191 if (msg.wParam == '\r' || msg.wParam == ' ')
3193 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3194 fEndMenu = (executedMenuId != -2);
3196 break;
3199 /* Hack to avoid control chars. */
3200 /* We will find a better way real soon... */
3201 if (msg.wParam < 32) break;
3203 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3204 LOWORD(msg.wParam), FALSE );
3205 if (pos == (UINT)-2) fEndMenu = TRUE;
3206 else if (pos == (UINT)-1) MessageBeep(0);
3207 else
3209 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3210 TRUE, 0 );
3211 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3212 fEndMenu = (executedMenuId != -2);
3215 break;
3216 } /* switch(msg.message) - kbd */
3218 else
3220 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3221 DispatchMessageW( &msg );
3222 continue;
3225 if (!fEndMenu) fRemove = TRUE;
3227 /* finally remove message from the queue */
3229 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3230 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3231 else mt.trackFlags &= ~TF_SKIPREMOVE;
3234 set_capture_window( 0, GUI_INMENUMODE, NULL );
3236 /* If dropdown is still painted and the close box is clicked on
3237 then the menu will be destroyed as part of the DispatchMessage above.
3238 This will then invalidate the menu handle in mt.hTopMenu. We should
3239 check for this first. */
3240 if( IsMenu( mt.hTopMenu ) )
3242 menu = MENU_GetMenu( mt.hTopMenu );
3244 if( IsWindow( mt.hOwnerWnd ) )
3246 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3248 if (menu && (menu->wFlags & MF_POPUP))
3250 DestroyWindow( menu->hWnd );
3251 menu->hWnd = 0;
3253 if (!(wFlags & TPM_NONOTIFY))
3254 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3255 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3257 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3258 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3261 /* Reset the variable for hiding menu */
3262 if( menu ) menu->bTimeToHide = FALSE;
3265 /* The return value is only used by TrackPopupMenu */
3266 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3267 if (executedMenuId == -1) executedMenuId = 0;
3268 return executedMenuId;
3271 /***********************************************************************
3272 * MENU_InitTracking
3274 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3276 POPUPMENU *menu;
3278 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3280 HideCaret(0);
3282 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3283 if (!(wFlags & TPM_NONOTIFY))
3284 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3286 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3288 if (!(wFlags & TPM_NONOTIFY))
3290 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3291 /* If an app changed/recreated menu bar entries in WM_INITMENU
3292 * menu sizes will be recalculated once the menu created/shown.
3296 /* This makes the menus of applications built with Delphi work.
3297 * It also enables menus to be displayed in more than one window,
3298 * but there are some bugs left that need to be fixed in this case.
3300 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3302 return TRUE;
3304 /***********************************************************************
3305 * MENU_ExitTracking
3307 static BOOL MENU_ExitTracking(HWND hWnd)
3309 TRACE("hwnd=%p\n", hWnd);
3311 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3312 ShowCaret(0);
3313 top_popup = 0;
3314 return TRUE;
3317 /***********************************************************************
3318 * MENU_TrackMouseMenuBar
3320 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3322 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3324 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3325 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3327 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3329 if (IsMenu(hMenu))
3331 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3332 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3333 MENU_ExitTracking(hWnd);
3338 /***********************************************************************
3339 * MENU_TrackKbdMenuBar
3341 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3343 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3345 UINT uItem = NO_SELECTED_ITEM;
3346 HMENU hTrackMenu;
3347 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3349 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3351 /* find window that has a menu */
3353 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3354 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3356 /* check if we have to track a system menu */
3358 hTrackMenu = GetMenu( hwnd );
3359 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3361 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3362 hTrackMenu = get_win_sys_menu( hwnd );
3363 uItem = 0;
3364 wParam |= HTSYSMENU; /* prevent item lookup */
3367 if (!IsMenu( hTrackMenu )) return;
3369 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3371 if( wChar && wChar != ' ' )
3373 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3374 if ( uItem >= (UINT)(-2) )
3376 if( uItem == (UINT)(-1) ) MessageBeep(0);
3377 /* schedule end of menu tracking */
3378 wFlags |= TF_ENDMENU;
3379 goto track_menu;
3383 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3385 if (!(wParam & HTSYSMENU) || wChar == ' ')
3387 if( uItem == NO_SELECTED_ITEM )
3388 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3389 else
3390 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3393 track_menu:
3394 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3395 MENU_ExitTracking( hwnd );
3399 /**********************************************************************
3400 * TrackPopupMenu (USER32.@)
3402 * Like the win32 API, the function return the command ID only if the
3403 * flag TPM_RETURNCMD is on.
3406 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3407 INT nReserved, HWND hWnd, const RECT *lpRect )
3409 BOOL ret = FALSE;
3411 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3412 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3414 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3416 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3417 if (!(wFlags & TPM_NONOTIFY))
3418 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3420 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3421 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3422 MENU_ExitTracking(hWnd);
3424 return ret;
3427 /**********************************************************************
3428 * TrackPopupMenuEx (USER32.@)
3430 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3431 HWND hWnd, LPTPMPARAMS lpTpm )
3433 FIXME("not fully implemented\n" );
3434 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3435 lpTpm ? &lpTpm->rcExclude : NULL );
3438 /***********************************************************************
3439 * PopupMenuWndProc
3441 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3443 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3445 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3447 switch(message)
3449 case WM_CREATE:
3451 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3452 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3453 return 0;
3456 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3457 return MA_NOACTIVATE;
3459 case WM_PAINT:
3461 PAINTSTRUCT ps;
3462 BeginPaint( hwnd, &ps );
3463 MENU_DrawPopupMenu( hwnd, ps.hdc,
3464 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3465 EndPaint( hwnd, &ps );
3466 return 0;
3468 case WM_ERASEBKGND:
3469 return 1;
3471 case WM_DESTROY:
3472 /* zero out global pointer in case resident popup window was destroyed. */
3473 if (hwnd == top_popup) top_popup = 0;
3474 break;
3476 case WM_SHOWWINDOW:
3478 if( wParam )
3480 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3482 else
3483 SetWindowLongPtrW( hwnd, 0, 0 );
3484 break;
3486 case MM_SETMENUHANDLE:
3487 SetWindowLongPtrW( hwnd, 0, wParam );
3488 break;
3490 case MM_GETMENUHANDLE:
3491 return GetWindowLongPtrW( hwnd, 0 );
3493 default:
3494 return DefWindowProcW( hwnd, message, wParam, lParam );
3496 return 0;
3500 /***********************************************************************
3501 * MENU_GetMenuBarHeight
3503 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3505 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3506 INT orgX, INT orgY )
3508 HDC hdc;
3509 RECT rectBar;
3510 LPPOPUPMENU lppop;
3512 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3514 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3516 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3517 SelectObject( hdc, get_menu_font(FALSE));
3518 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3519 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3520 ReleaseDC( hwnd, hdc );
3521 return lppop->Height;
3525 /*******************************************************************
3526 * ChangeMenuA (USER32.@)
3528 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3529 UINT id, UINT flags )
3531 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3532 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3533 id, data );
3534 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3535 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3536 id, data );
3537 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3538 flags & MF_BYPOSITION ? pos : id,
3539 flags & ~MF_REMOVE );
3540 /* Default: MF_INSERT */
3541 return InsertMenuA( hMenu, pos, flags, id, data );
3545 /*******************************************************************
3546 * ChangeMenuW (USER32.@)
3548 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3549 UINT id, UINT flags )
3551 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3552 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3553 id, data );
3554 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3555 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3556 id, data );
3557 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3558 flags & MF_BYPOSITION ? pos : id,
3559 flags & ~MF_REMOVE );
3560 /* Default: MF_INSERT */
3561 return InsertMenuW( hMenu, pos, flags, id, data );
3565 /*******************************************************************
3566 * CheckMenuItem (USER32.@)
3568 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3570 MENUITEM *item;
3571 DWORD ret;
3573 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3574 ret = item->fState & MF_CHECKED;
3575 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3576 else item->fState &= ~MF_CHECKED;
3577 return ret;
3581 /**********************************************************************
3582 * EnableMenuItem (USER32.@)
3584 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3586 UINT oldflags;
3587 MENUITEM *item;
3588 POPUPMENU *menu;
3590 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3592 /* Get the Popupmenu to access the owner menu */
3593 if (!(menu = MENU_GetMenu(hMenu)))
3594 return (UINT)-1;
3596 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3597 return (UINT)-1;
3599 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3600 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3602 /* If the close item in the system menu change update the close button */
3603 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3605 if (menu->hSysMenuOwner != 0)
3607 RECT rc;
3608 POPUPMENU* parentMenu;
3610 /* Get the parent menu to access*/
3611 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3612 return (UINT)-1;
3614 /* Refresh the frame to reflect the change */
3615 GetWindowRect(parentMenu->hWnd, &rc);
3616 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3617 rc.bottom = 0;
3618 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3622 return oldflags;
3626 /*******************************************************************
3627 * GetMenuStringA (USER32.@)
3629 INT WINAPI GetMenuStringA(
3630 HMENU hMenu, /* [in] menuhandle */
3631 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3632 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3633 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3634 UINT wFlags /* [in] MF_ flags */
3636 MENUITEM *item;
3638 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3639 if (str && nMaxSiz) str[0] = '\0';
3640 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3641 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3642 return 0;
3644 if (!item->text) return 0;
3645 if (!str || !nMaxSiz) return strlenW(item->text);
3646 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3647 str[nMaxSiz-1] = 0;
3648 TRACE("returning %s\n", debugstr_a(str));
3649 return strlen(str);
3653 /*******************************************************************
3654 * GetMenuStringW (USER32.@)
3656 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3657 LPWSTR str, INT nMaxSiz, UINT wFlags )
3659 MENUITEM *item;
3661 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3662 if (str && nMaxSiz) str[0] = '\0';
3663 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3664 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3665 return 0;
3667 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3668 if( !(item->text)) {
3669 str[0] = 0;
3670 return 0;
3672 lstrcpynW( str, item->text, nMaxSiz );
3673 TRACE("returning %s\n", debugstr_w(str));
3674 return strlenW(str);
3678 /**********************************************************************
3679 * HiliteMenuItem (USER32.@)
3681 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3682 UINT wHilite )
3684 LPPOPUPMENU menu;
3685 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3686 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3687 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3688 if (menu->FocusedItem == wItemID) return TRUE;
3689 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3690 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3691 return TRUE;
3695 /**********************************************************************
3696 * GetMenuState (USER32.@)
3698 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3700 MENUITEM *item;
3701 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3702 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3703 debug_print_menuitem (" item: ", item, "");
3704 if (item->fType & MF_POPUP)
3706 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3707 if (!menu) return -1;
3708 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3710 else
3712 /* We used to (from way back then) mask the result to 0xff. */
3713 /* I don't know why and it seems wrong as the documented */
3714 /* return flag MF_SEPARATOR is outside that mask. */
3715 return (item->fType | item->fState);
3720 /**********************************************************************
3721 * GetMenuItemCount (USER32.@)
3723 INT WINAPI GetMenuItemCount( HMENU hMenu )
3725 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3726 if (!menu) return -1;
3727 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3728 return menu->nItems;
3732 /**********************************************************************
3733 * GetMenuItemID (USER32.@)
3735 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3737 MENUITEM * lpmi;
3739 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3740 if (lpmi->fType & MF_POPUP) return -1;
3741 return lpmi->wID;
3746 /*******************************************************************
3747 * InsertMenuW (USER32.@)
3749 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3750 UINT_PTR id, LPCWSTR str )
3752 MENUITEM *item;
3754 if (IS_STRING_ITEM(flags) && str)
3755 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3756 hMenu, pos, flags, id, debugstr_w(str) );
3757 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3758 hMenu, pos, flags, id, str );
3760 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3762 if (!(MENU_SetItemData( item, flags, id, str )))
3764 RemoveMenu( hMenu, pos, flags );
3765 return FALSE;
3768 item->hCheckBit = item->hUnCheckBit = 0;
3769 return TRUE;
3773 /*******************************************************************
3774 * InsertMenuA (USER32.@)
3776 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3777 UINT_PTR id, LPCSTR str )
3779 BOOL ret = FALSE;
3781 if (IS_STRING_ITEM(flags) && str)
3783 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3784 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3785 if (newstr)
3787 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3788 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3789 HeapFree( GetProcessHeap(), 0, newstr );
3791 return ret;
3793 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3797 /*******************************************************************
3798 * AppendMenuA (USER32.@)
3800 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3801 UINT_PTR id, LPCSTR data )
3803 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3807 /*******************************************************************
3808 * AppendMenuW (USER32.@)
3810 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3811 UINT_PTR id, LPCWSTR data )
3813 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3817 /**********************************************************************
3818 * RemoveMenu (USER32.@)
3820 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3822 LPPOPUPMENU menu;
3823 MENUITEM *item;
3825 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3826 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3827 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3829 /* Remove item */
3831 MENU_FreeItemData( item );
3833 if (--menu->nItems == 0)
3835 HeapFree( GetProcessHeap(), 0, menu->items );
3836 menu->items = NULL;
3838 else
3840 while(nPos < menu->nItems)
3842 *item = *(item+1);
3843 item++;
3844 nPos++;
3846 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3847 menu->nItems * sizeof(MENUITEM) );
3849 return TRUE;
3853 /**********************************************************************
3854 * DeleteMenu (USER32.@)
3856 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3858 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3859 if (!item) return FALSE;
3860 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3861 /* nPos is now the position of the item */
3862 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3863 return TRUE;
3867 /*******************************************************************
3868 * ModifyMenuW (USER32.@)
3870 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3871 UINT_PTR id, LPCWSTR str )
3873 MENUITEM *item;
3875 if (IS_STRING_ITEM(flags))
3876 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3877 else
3878 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3880 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3881 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3882 return MENU_SetItemData( item, flags, id, str );
3886 /*******************************************************************
3887 * ModifyMenuA (USER32.@)
3889 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3890 UINT_PTR id, LPCSTR str )
3892 BOOL ret = FALSE;
3894 if (IS_STRING_ITEM(flags) && str)
3896 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3897 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3898 if (newstr)
3900 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3901 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3902 HeapFree( GetProcessHeap(), 0, newstr );
3904 return ret;
3906 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3910 /**********************************************************************
3911 * CreatePopupMenu (USER32.@)
3913 HMENU WINAPI CreatePopupMenu(void)
3915 HMENU hmenu;
3916 POPUPMENU *menu;
3918 if (!(hmenu = CreateMenu())) return 0;
3919 menu = MENU_GetMenu( hmenu );
3920 menu->wFlags |= MF_POPUP;
3921 menu->bTimeToHide = FALSE;
3922 return hmenu;
3926 /**********************************************************************
3927 * GetMenuCheckMarkDimensions (USER.417)
3928 * GetMenuCheckMarkDimensions (USER32.@)
3930 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3932 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3936 /**********************************************************************
3937 * SetMenuItemBitmaps (USER32.@)
3939 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3940 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3942 MENUITEM *item;
3944 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3946 if (!hNewCheck && !hNewUnCheck)
3948 item->fState &= ~MF_USECHECKBITMAPS;
3950 else /* Install new bitmaps */
3952 item->hCheckBit = hNewCheck;
3953 item->hUnCheckBit = hNewUnCheck;
3954 item->fState |= MF_USECHECKBITMAPS;
3956 return TRUE;
3960 /**********************************************************************
3961 * CreateMenu (USER32.@)
3963 HMENU WINAPI CreateMenu(void)
3965 HMENU hMenu;
3966 LPPOPUPMENU menu;
3967 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3968 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3970 ZeroMemory(menu, sizeof(POPUPMENU));
3971 menu->wMagic = MENU_MAGIC;
3972 menu->FocusedItem = NO_SELECTED_ITEM;
3973 menu->bTimeToHide = FALSE;
3975 TRACE("return %p\n", hMenu );
3977 return hMenu;
3981 /**********************************************************************
3982 * DestroyMenu (USER32.@)
3984 BOOL WINAPI DestroyMenu( HMENU hMenu )
3986 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3988 TRACE("(%p)\n", hMenu);
3991 if (!lppop) return FALSE;
3993 lppop->wMagic = 0; /* Mark it as destroyed */
3995 /* DestroyMenu should not destroy system menu popup owner */
3996 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3998 DestroyWindow( lppop->hWnd );
3999 lppop->hWnd = 0;
4002 if (lppop->items) /* recursively destroy submenus */
4004 int i;
4005 MENUITEM *item = lppop->items;
4006 for (i = lppop->nItems; i > 0; i--, item++)
4008 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4009 MENU_FreeItemData( item );
4011 HeapFree( GetProcessHeap(), 0, lppop->items );
4013 USER_HEAP_FREE( hMenu );
4014 return TRUE;
4018 /**********************************************************************
4019 * GetSystemMenu (USER32.@)
4021 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4023 WND *wndPtr = WIN_GetPtr( hWnd );
4024 HMENU retvalue = 0;
4026 if (wndPtr == WND_DESKTOP) return 0;
4027 if (wndPtr == WND_OTHER_PROCESS)
4029 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4031 else if (wndPtr)
4033 if (wndPtr->hSysMenu && bRevert)
4035 DestroyMenu(wndPtr->hSysMenu);
4036 wndPtr->hSysMenu = 0;
4039 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4040 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4042 if( wndPtr->hSysMenu )
4044 POPUPMENU *menu;
4045 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4047 /* Store the dummy sysmenu handle to facilitate the refresh */
4048 /* of the close button if the SC_CLOSE item change */
4049 menu = MENU_GetMenu(retvalue);
4050 if ( menu )
4051 menu->hSysMenuOwner = wndPtr->hSysMenu;
4053 WIN_ReleasePtr( wndPtr );
4055 return bRevert ? 0 : retvalue;
4059 /*******************************************************************
4060 * SetSystemMenu (USER32.@)
4062 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4064 WND *wndPtr = WIN_GetPtr( hwnd );
4066 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4068 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4069 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4070 WIN_ReleasePtr( wndPtr );
4071 return TRUE;
4073 return FALSE;
4077 /**********************************************************************
4078 * GetMenu (USER32.@)
4080 HMENU WINAPI GetMenu( HWND hWnd )
4082 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4083 TRACE("for %p returning %p\n", hWnd, retvalue);
4084 return retvalue;
4087 /**********************************************************************
4088 * GetMenuBarInfo (USER32.@)
4090 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4092 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4093 return FALSE;
4096 /**********************************************************************
4097 * MENU_SetMenu
4099 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4100 * SetWindowPos call that would result if SetMenu were called directly.
4102 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4104 TRACE("(%p, %p);\n", hWnd, hMenu);
4106 if (hMenu && !IsMenu(hMenu))
4108 WARN("hMenu %p is not a menu handle\n", hMenu);
4109 return FALSE;
4111 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4112 return FALSE;
4114 hWnd = WIN_GetFullHandle( hWnd );
4115 if (GetCapture() == hWnd)
4116 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4118 if (hMenu != 0)
4120 LPPOPUPMENU lpmenu;
4122 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4124 lpmenu->hWnd = hWnd;
4125 lpmenu->Height = 0; /* Make sure we recalculate the size */
4127 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4128 return TRUE;
4132 /**********************************************************************
4133 * SetMenu (USER32.@)
4135 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4137 if(!MENU_SetMenu(hWnd, hMenu))
4138 return FALSE;
4140 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4141 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4142 return TRUE;
4146 /**********************************************************************
4147 * GetSubMenu (USER32.@)
4149 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4151 MENUITEM * lpmi;
4153 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4154 if (!(lpmi->fType & MF_POPUP)) return 0;
4155 return lpmi->hSubMenu;
4159 /**********************************************************************
4160 * DrawMenuBar (USER32.@)
4162 BOOL WINAPI DrawMenuBar( HWND hWnd )
4164 LPPOPUPMENU lppop;
4165 HMENU hMenu = GetMenu(hWnd);
4167 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4168 return FALSE;
4169 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4171 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4172 lppop->hwndOwner = hWnd;
4173 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4174 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4175 return TRUE;
4178 /***********************************************************************
4179 * DrawMenuBarTemp (USER32.@)
4181 * UNDOCUMENTED !!
4183 * called by W98SE desk.cpl Control Panel Applet
4185 * Not 100% sure about the param names, but close.
4187 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4189 LPPOPUPMENU lppop;
4190 UINT i,retvalue;
4191 HFONT hfontOld = 0;
4192 BOOL flat_menu = FALSE;
4194 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4196 if (!hMenu)
4197 hMenu = GetMenu(hwnd);
4199 if (!hFont)
4200 hFont = get_menu_font(FALSE);
4202 lppop = MENU_GetMenu( hMenu );
4203 if (lppop == NULL || lprect == NULL)
4205 retvalue = GetSystemMetrics(SM_CYMENU);
4206 goto END;
4209 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4211 hfontOld = SelectObject( hDC, hFont);
4213 if (lppop->Height == 0)
4214 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4216 lprect->bottom = lprect->top + lppop->Height;
4218 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4220 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4221 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4222 LineTo( hDC, lprect->right, lprect->bottom );
4224 if (lppop->nItems == 0)
4226 retvalue = GetSystemMetrics(SM_CYMENU);
4227 goto END;
4230 for (i = 0; i < lppop->nItems; i++)
4232 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4233 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4235 retvalue = lppop->Height;
4237 END:
4238 if (hfontOld) SelectObject (hDC, hfontOld);
4239 return retvalue;
4242 /***********************************************************************
4243 * EndMenu (USER.187)
4244 * EndMenu (USER32.@)
4246 BOOL WINAPI EndMenu(void)
4248 /* if we are in the menu code, and it is active */
4249 if (!fEndMenu && top_popup)
4251 /* terminate the menu handling code */
4252 fEndMenu = TRUE;
4254 /* needs to be posted to wakeup the internal menu handler */
4255 /* which will now terminate the menu, in the event that */
4256 /* the main window was minimized, or lost focus, so we */
4257 /* don't end up with an orphaned menu */
4258 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4260 return fEndMenu;
4264 /***********************************************************************
4265 * LookupMenuHandle (USER.217)
4267 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4269 HMENU hmenu32 = HMENU_32(hmenu);
4270 UINT id32 = id;
4271 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4272 else return HMENU_16(hmenu32);
4276 /**********************************************************************
4277 * LoadMenu (USER.150)
4279 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4281 HRSRC16 hRsrc;
4282 HGLOBAL16 handle;
4283 HMENU16 hMenu;
4285 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4286 if (!name) return 0;
4288 instance = GetExePtr( instance );
4289 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4290 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4291 hMenu = LoadMenuIndirect16(LockResource16(handle));
4292 FreeResource16( handle );
4293 return hMenu;
4297 /*****************************************************************
4298 * LoadMenuA (USER32.@)
4300 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4302 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4303 if (!hrsrc) return 0;
4304 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4308 /*****************************************************************
4309 * LoadMenuW (USER32.@)
4311 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4313 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4314 if (!hrsrc) return 0;
4315 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4319 /**********************************************************************
4320 * LoadMenuIndirect (USER.220)
4322 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4324 HMENU hMenu;
4325 WORD version, offset;
4326 LPCSTR p = (LPCSTR)template;
4328 TRACE("(%p)\n", template );
4329 version = GET_WORD(p);
4330 p += sizeof(WORD);
4331 if (version)
4333 WARN("version must be 0 for Win16\n" );
4334 return 0;
4336 offset = GET_WORD(p);
4337 p += sizeof(WORD) + offset;
4338 if (!(hMenu = CreateMenu())) return 0;
4339 if (!MENU_ParseResource( p, hMenu, FALSE ))
4341 DestroyMenu( hMenu );
4342 return 0;
4344 return HMENU_16(hMenu);
4348 /**********************************************************************
4349 * LoadMenuIndirectW (USER32.@)
4351 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4353 HMENU hMenu;
4354 WORD version, offset;
4355 LPCSTR p = (LPCSTR)template;
4357 version = GET_WORD(p);
4358 p += sizeof(WORD);
4359 TRACE("%p, ver %d\n", template, version );
4360 switch (version)
4362 case 0: /* standard format is version of 0 */
4363 offset = GET_WORD(p);
4364 p += sizeof(WORD) + offset;
4365 if (!(hMenu = CreateMenu())) return 0;
4366 if (!MENU_ParseResource( p, hMenu, TRUE ))
4368 DestroyMenu( hMenu );
4369 return 0;
4371 return hMenu;
4372 case 1: /* extended format is version of 1 */
4373 offset = GET_WORD(p);
4374 p += sizeof(WORD) + offset;
4375 if (!(hMenu = CreateMenu())) return 0;
4376 if (!MENUEX_ParseResource( p, hMenu))
4378 DestroyMenu( hMenu );
4379 return 0;
4381 return hMenu;
4382 default:
4383 ERR("version %d not supported.\n", version);
4384 return 0;
4389 /**********************************************************************
4390 * LoadMenuIndirectA (USER32.@)
4392 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4394 return LoadMenuIndirectW( template );
4398 /**********************************************************************
4399 * IsMenu (USER32.@)
4401 BOOL WINAPI IsMenu(HMENU hmenu)
4403 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4405 if (!menu)
4407 SetLastError(ERROR_INVALID_MENU_HANDLE);
4408 return FALSE;
4410 return TRUE;
4413 /**********************************************************************
4414 * GetMenuItemInfo_common
4417 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4418 LPMENUITEMINFOW lpmii, BOOL unicode)
4420 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4422 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4424 if (!menu)
4425 return FALSE;
4427 if( lpmii->fMask & MIIM_TYPE) {
4428 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4429 WARN("invalid combination of fMask bits used\n");
4430 /* this does not happen on Win9x/ME */
4431 SetLastError( ERROR_INVALID_PARAMETER);
4432 return FALSE;
4434 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4435 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4436 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4437 if( lpmii->fType & MFT_BITMAP) {
4438 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4439 lpmii->cch = 0;
4440 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4441 /* this does not happen on Win9x/ME */
4442 lpmii->dwTypeData = 0;
4443 lpmii->cch = 0;
4447 /* copy the text string */
4448 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4449 if( !menu->text ) {
4450 if(lpmii->dwTypeData && lpmii->cch) {
4451 lpmii->cch = 0;
4452 if( unicode)
4453 *((WCHAR *)lpmii->dwTypeData) = 0;
4454 else
4455 *((CHAR *)lpmii->dwTypeData) = 0;
4457 } else {
4458 int len;
4459 if (unicode)
4461 len = strlenW(menu->text);
4462 if(lpmii->dwTypeData && lpmii->cch)
4463 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4465 else
4467 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4468 0, NULL, NULL ) - 1;
4469 if(lpmii->dwTypeData && lpmii->cch)
4470 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4471 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4472 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4474 /* if we've copied a substring we return its length */
4475 if(lpmii->dwTypeData && lpmii->cch)
4476 if (lpmii->cch <= len + 1)
4477 lpmii->cch--;
4478 else
4479 lpmii->cch = len;
4480 else {
4481 /* return length of string */
4482 /* not on Win9x/ME if fType & MFT_BITMAP */
4483 lpmii->cch = len;
4488 if (lpmii->fMask & MIIM_FTYPE)
4489 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4491 if (lpmii->fMask & MIIM_BITMAP)
4492 lpmii->hbmpItem = menu->hbmpItem;
4494 if (lpmii->fMask & MIIM_STATE)
4495 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4497 if (lpmii->fMask & MIIM_ID)
4498 lpmii->wID = menu->wID;
4500 if (lpmii->fMask & MIIM_SUBMENU)
4501 lpmii->hSubMenu = menu->hSubMenu;
4502 else {
4503 /* hSubMenu is always cleared
4504 * (not on Win9x/ME ) */
4505 lpmii->hSubMenu = 0;
4508 if (lpmii->fMask & MIIM_CHECKMARKS) {
4509 lpmii->hbmpChecked = menu->hCheckBit;
4510 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4512 if (lpmii->fMask & MIIM_DATA)
4513 lpmii->dwItemData = menu->dwItemData;
4515 return TRUE;
4518 /**********************************************************************
4519 * GetMenuItemInfoA (USER32.@)
4521 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4522 LPMENUITEMINFOA lpmii)
4524 BOOL ret;
4525 MENUITEMINFOA mii;
4526 if( lpmii->cbSize != sizeof( mii) &&
4527 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4528 SetLastError( ERROR_INVALID_PARAMETER);
4529 return FALSE;
4531 memcpy( &mii, lpmii, lpmii->cbSize);
4532 mii.cbSize = sizeof( mii);
4533 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4534 (LPMENUITEMINFOW)&mii, FALSE);
4535 mii.cbSize = lpmii->cbSize;
4536 memcpy( lpmii, &mii, mii.cbSize);
4537 return ret;
4540 /**********************************************************************
4541 * GetMenuItemInfoW (USER32.@)
4543 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4544 LPMENUITEMINFOW lpmii)
4546 BOOL ret;
4547 MENUITEMINFOW mii;
4548 if( lpmii->cbSize != sizeof( mii) &&
4549 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4550 SetLastError( ERROR_INVALID_PARAMETER);
4551 return FALSE;
4553 memcpy( &mii, lpmii, lpmii->cbSize);
4554 mii.cbSize = sizeof( mii);
4555 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4556 mii.cbSize = lpmii->cbSize;
4557 memcpy( lpmii, &mii, mii.cbSize);
4558 return ret;
4562 /* set a menu item text from a ASCII or Unicode string */
4563 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4565 if (!text)
4566 menu->text = NULL;
4567 else if (unicode)
4569 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4570 strcpyW( menu->text, text );
4572 else
4574 LPCSTR str = (LPCSTR)text;
4575 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4576 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4577 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4582 /**********************************************************************
4583 * SetMenuItemInfo_common
4586 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4587 const MENUITEMINFOW *lpmii,
4588 BOOL unicode)
4590 if (!menu) return FALSE;
4592 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4594 if (lpmii->fMask & MIIM_TYPE ) {
4595 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4596 WARN("invalid combination of fMask bits used\n");
4597 /* this does not happen on Win9x/ME */
4598 SetLastError( ERROR_INVALID_PARAMETER);
4599 return FALSE;
4602 /* Remove the old type bits and replace them with the new ones */
4603 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4604 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4606 if (IS_STRING_ITEM(menu->fType)) {
4607 HeapFree(GetProcessHeap(), 0, menu->text);
4608 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4609 } else if( (menu->fType) & MFT_BITMAP)
4610 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4613 if (lpmii->fMask & MIIM_FTYPE ) {
4614 if(( lpmii->fType & MFT_BITMAP)) {
4615 SetLastError( ERROR_INVALID_PARAMETER);
4616 return FALSE;
4618 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4619 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4621 if (lpmii->fMask & MIIM_STRING ) {
4622 /* free the string when used */
4623 HeapFree(GetProcessHeap(), 0, menu->text);
4624 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4627 if (lpmii->fMask & MIIM_STATE)
4629 /* Other menu items having MFS_DEFAULT are not converted
4630 to normal items */
4631 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4634 if (lpmii->fMask & MIIM_ID)
4635 menu->wID = lpmii->wID;
4637 if (lpmii->fMask & MIIM_SUBMENU) {
4638 menu->hSubMenu = lpmii->hSubMenu;
4639 if (menu->hSubMenu) {
4640 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4641 if (subMenu) {
4642 subMenu->wFlags |= MF_POPUP;
4643 menu->fType |= MF_POPUP;
4645 else {
4646 SetLastError( ERROR_INVALID_PARAMETER);
4647 return FALSE;
4650 else
4651 menu->fType &= ~MF_POPUP;
4654 if (lpmii->fMask & MIIM_CHECKMARKS)
4656 menu->hCheckBit = lpmii->hbmpChecked;
4657 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4659 if (lpmii->fMask & MIIM_DATA)
4660 menu->dwItemData = lpmii->dwItemData;
4662 if (lpmii->fMask & MIIM_BITMAP)
4663 menu->hbmpItem = lpmii->hbmpItem;
4665 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4666 menu->fType |= MFT_SEPARATOR;
4668 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4669 return TRUE;
4672 /**********************************************************************
4673 * SetMenuItemInfoA (USER32.@)
4675 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4676 const MENUITEMINFOA *lpmii)
4678 MENUITEMINFOA mii;
4680 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4682 if( lpmii->cbSize != sizeof( mii) &&
4683 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4684 SetLastError( ERROR_INVALID_PARAMETER);
4685 return FALSE;
4687 memcpy( &mii, lpmii, lpmii->cbSize);
4688 if( lpmii->cbSize != sizeof( mii)) {
4689 mii.cbSize = sizeof( mii);
4690 mii.hbmpItem = NULL;
4692 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4693 (const MENUITEMINFOW *)&mii, FALSE);
4696 /**********************************************************************
4697 * SetMenuItemInfoW (USER32.@)
4699 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4700 const MENUITEMINFOW *lpmii)
4702 MENUITEMINFOW mii;
4704 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4706 if( lpmii->cbSize != sizeof( mii) &&
4707 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4708 SetLastError( ERROR_INVALID_PARAMETER);
4709 return FALSE;
4711 memcpy( &mii, lpmii, lpmii->cbSize);
4712 if( lpmii->cbSize != sizeof( mii)) {
4713 mii.cbSize = sizeof( mii);
4714 mii.hbmpItem = NULL;
4716 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4717 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4720 /**********************************************************************
4721 * SetMenuDefaultItem (USER32.@)
4724 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4726 UINT i;
4727 POPUPMENU *menu;
4728 MENUITEM *item;
4730 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4732 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4734 /* reset all default-item flags */
4735 item = menu->items;
4736 for (i = 0; i < menu->nItems; i++, item++)
4738 item->fState &= ~MFS_DEFAULT;
4741 /* no default item */
4742 if ( -1 == uItem)
4744 return TRUE;
4747 item = menu->items;
4748 if ( bypos )
4750 if ( uItem >= menu->nItems ) return FALSE;
4751 item[uItem].fState |= MFS_DEFAULT;
4752 return TRUE;
4754 else
4756 for (i = 0; i < menu->nItems; i++, item++)
4758 if (item->wID == uItem)
4760 item->fState |= MFS_DEFAULT;
4761 return TRUE;
4766 return FALSE;
4769 /**********************************************************************
4770 * GetMenuDefaultItem (USER32.@)
4772 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4774 POPUPMENU *menu;
4775 MENUITEM * item;
4776 UINT i = 0;
4778 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4780 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4782 /* find default item */
4783 item = menu->items;
4785 /* empty menu */
4786 if (! item) return -1;
4788 while ( !( item->fState & MFS_DEFAULT ) )
4790 i++; item++;
4791 if (i >= menu->nItems ) return -1;
4794 /* default: don't return disabled items */
4795 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4797 /* search rekursiv when needed */
4798 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4800 UINT ret;
4801 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4802 if ( -1 != ret ) return ret;
4804 /* when item not found in submenu, return the popup item */
4806 return ( bypos ) ? i : item->wID;
4811 /**********************************************************************
4812 * InsertMenuItemA (USER32.@)
4814 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4815 const MENUITEMINFOA *lpmii)
4817 MENUITEM *item;
4818 MENUITEMINFOA mii;
4820 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4822 if( lpmii->cbSize != sizeof( mii) &&
4823 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4824 SetLastError( ERROR_INVALID_PARAMETER);
4825 return FALSE;
4827 memcpy( &mii, lpmii, lpmii->cbSize);
4828 if( lpmii->cbSize != sizeof( mii)) {
4829 mii.cbSize = sizeof( mii);
4830 mii.hbmpItem = NULL;
4833 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4834 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4838 /**********************************************************************
4839 * InsertMenuItemW (USER32.@)
4841 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4842 const MENUITEMINFOW *lpmii)
4844 MENUITEM *item;
4845 MENUITEMINFOW mii;
4847 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4849 if( lpmii->cbSize != sizeof( mii) &&
4850 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4851 SetLastError( ERROR_INVALID_PARAMETER);
4852 return FALSE;
4854 memcpy( &mii, lpmii, lpmii->cbSize);
4855 if( lpmii->cbSize != sizeof( mii)) {
4856 mii.cbSize = sizeof( mii);
4857 mii.hbmpItem = NULL;
4860 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4861 return SetMenuItemInfo_common(item, &mii, TRUE);
4864 /**********************************************************************
4865 * CheckMenuRadioItem (USER32.@)
4868 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4869 UINT first, UINT last, UINT check,
4870 UINT bypos)
4872 BOOL done = FALSE;
4873 UINT i;
4874 MENUITEM *mi_first = NULL, *mi_check;
4875 HMENU m_first, m_check;
4877 for (i = first; i <= last; i++)
4879 UINT pos = i;
4881 if (!mi_first)
4883 m_first = hMenu;
4884 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4885 if (!mi_first) continue;
4886 mi_check = mi_first;
4887 m_check = m_first;
4889 else
4891 m_check = hMenu;
4892 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4893 if (!mi_check) continue;
4896 if (m_first != m_check) continue;
4897 if (mi_check->fType == MFT_SEPARATOR) continue;
4899 if (i == check)
4901 mi_check->fType |= MFT_RADIOCHECK;
4902 mi_check->fState |= MFS_CHECKED;
4903 done = TRUE;
4905 else
4907 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4908 mi_check->fState &= ~MFS_CHECKED;
4912 return done;
4916 /**********************************************************************
4917 * GetMenuItemRect (USER32.@)
4919 * ATTENTION: Here, the returned values in rect are the screen
4920 * coordinates of the item just like if the menu was
4921 * always on the upper left side of the application.
4924 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4925 LPRECT rect)
4927 POPUPMENU *itemMenu;
4928 MENUITEM *item;
4929 HWND referenceHwnd;
4931 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4933 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4934 referenceHwnd = hwnd;
4936 if(!hwnd)
4938 itemMenu = MENU_GetMenu(hMenu);
4939 if (itemMenu == NULL)
4940 return FALSE;
4942 if(itemMenu->hWnd == 0)
4943 return FALSE;
4944 referenceHwnd = itemMenu->hWnd;
4947 if ((rect == NULL) || (item == NULL))
4948 return FALSE;
4950 *rect = item->rect;
4952 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4954 return TRUE;
4958 /**********************************************************************
4959 * SetMenuInfo (USER32.@)
4961 * FIXME
4962 * MIM_APPLYTOSUBMENUS
4963 * actually use the items to draw the menu
4965 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4967 POPUPMENU *menu;
4969 TRACE("(%p %p)\n", hMenu, lpmi);
4971 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4974 if (lpmi->fMask & MIM_BACKGROUND)
4975 menu->hbrBack = lpmi->hbrBack;
4977 if (lpmi->fMask & MIM_HELPID)
4978 menu->dwContextHelpID = lpmi->dwContextHelpID;
4980 if (lpmi->fMask & MIM_MAXHEIGHT)
4981 menu->cyMax = lpmi->cyMax;
4983 if (lpmi->fMask & MIM_MENUDATA)
4984 menu->dwMenuData = lpmi->dwMenuData;
4986 if (lpmi->fMask & MIM_STYLE)
4988 menu->dwStyle = lpmi->dwStyle;
4989 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4990 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4991 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4994 return TRUE;
4996 return FALSE;
4999 /**********************************************************************
5000 * GetMenuInfo (USER32.@)
5002 * NOTES
5003 * win98/NT5.0
5006 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5007 { POPUPMENU *menu;
5009 TRACE("(%p %p)\n", hMenu, lpmi);
5011 if (lpmi && (menu = MENU_GetMenu(hMenu)))
5014 if (lpmi->fMask & MIM_BACKGROUND)
5015 lpmi->hbrBack = menu->hbrBack;
5017 if (lpmi->fMask & MIM_HELPID)
5018 lpmi->dwContextHelpID = menu->dwContextHelpID;
5020 if (lpmi->fMask & MIM_MAXHEIGHT)
5021 lpmi->cyMax = menu->cyMax;
5023 if (lpmi->fMask & MIM_MENUDATA)
5024 lpmi->dwMenuData = menu->dwMenuData;
5026 if (lpmi->fMask & MIM_STYLE)
5027 lpmi->dwStyle = menu->dwStyle;
5029 return TRUE;
5031 return FALSE;
5035 /**********************************************************************
5036 * SetMenuContextHelpId (USER32.@)
5038 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5040 LPPOPUPMENU menu;
5042 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5044 if ((menu = MENU_GetMenu(hMenu)))
5046 menu->dwContextHelpID = dwContextHelpID;
5047 return TRUE;
5049 return FALSE;
5053 /**********************************************************************
5054 * GetMenuContextHelpId (USER32.@)
5056 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5058 LPPOPUPMENU menu;
5060 TRACE("(%p)\n", hMenu);
5062 if ((menu = MENU_GetMenu(hMenu)))
5064 return menu->dwContextHelpID;
5066 return 0;
5069 /**********************************************************************
5070 * MenuItemFromPoint (USER32.@)
5072 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5074 POPUPMENU *menu = MENU_GetMenu(hMenu);
5075 UINT pos;
5077 /*FIXME: Do we have to handle hWnd here? */
5078 if (!menu) return -1;
5079 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5080 return pos;
5084 /**********************************************************************
5085 * translate_accelerator
5087 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5088 BYTE fVirt, WORD key, WORD cmd )
5090 INT mask = 0;
5091 UINT mesg = 0;
5093 if (wParam != key) return FALSE;
5095 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5096 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5097 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5099 if (message == WM_CHAR || message == WM_SYSCHAR)
5101 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5103 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5104 goto found;
5107 else
5109 if(fVirt & FVIRTKEY)
5111 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5112 wParam, 0xff & HIWORD(lParam));
5114 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5115 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5117 else
5119 if (!(lParam & 0x01000000)) /* no special_key */
5121 if ((fVirt & FALT) && (lParam & 0x20000000))
5122 { /* ^^ ALT pressed */
5123 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5124 goto found;
5129 return FALSE;
5131 found:
5132 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5133 mesg = 1;
5134 else
5136 HMENU hMenu, hSubMenu, hSysMenu;
5137 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5139 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5140 hSysMenu = get_win_sys_menu( hWnd );
5142 /* find menu item and ask application to initialize it */
5143 /* 1. in the system menu */
5144 hSubMenu = hSysMenu;
5145 nPos = cmd;
5146 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5148 if (GetCapture())
5149 mesg = 2;
5150 if (!IsWindowEnabled(hWnd))
5151 mesg = 3;
5152 else
5154 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5155 if(hSubMenu != hSysMenu)
5157 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5158 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5159 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5161 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5164 else /* 2. in the window's menu */
5166 hSubMenu = hMenu;
5167 nPos = cmd;
5168 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5170 if (GetCapture())
5171 mesg = 2;
5172 if (!IsWindowEnabled(hWnd))
5173 mesg = 3;
5174 else
5176 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5177 if(hSubMenu != hMenu)
5179 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5180 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5181 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5183 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5188 if (mesg == 0)
5190 if (uSysStat != (UINT)-1)
5192 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5193 mesg=4;
5194 else
5195 mesg=WM_SYSCOMMAND;
5197 else
5199 if (uStat != (UINT)-1)
5201 if (IsIconic(hWnd))
5202 mesg=5;
5203 else
5205 if (uStat & (MF_DISABLED|MF_GRAYED))
5206 mesg=6;
5207 else
5208 mesg=WM_COMMAND;
5211 else
5212 mesg=WM_COMMAND;
5217 if( mesg==WM_COMMAND )
5219 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5220 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5222 else if( mesg==WM_SYSCOMMAND )
5224 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5225 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5227 else
5229 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5230 * #0: unknown (please report!)
5231 * #1: for WM_KEYUP,WM_SYSKEYUP
5232 * #2: mouse is captured
5233 * #3: window is disabled
5234 * #4: it's a disabled system menu option
5235 * #5: it's a menu option, but window is iconic
5236 * #6: it's a menu option, but disabled
5238 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5239 if(mesg==0)
5240 ERR_(accel)(" unknown reason - please report!\n");
5242 return TRUE;
5245 /**********************************************************************
5246 * TranslateAcceleratorA (USER32.@)
5247 * TranslateAccelerator (USER32.@)
5249 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5251 /* YES, Accel16! */
5252 LPACCEL16 lpAccelTbl;
5253 int i;
5254 WPARAM wParam;
5256 if (!hWnd || !msg) return 0;
5258 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5260 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5261 return 0;
5264 wParam = msg->wParam;
5266 switch (msg->message)
5268 case WM_KEYDOWN:
5269 case WM_SYSKEYDOWN:
5270 break;
5272 case WM_CHAR:
5273 case WM_SYSCHAR:
5275 char ch = LOWORD(wParam);
5276 WCHAR wch;
5277 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5278 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5280 break;
5282 default:
5283 return 0;
5286 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5287 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5288 i = 0;
5291 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5292 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5293 return 1;
5294 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5296 return 0;
5299 /**********************************************************************
5300 * TranslateAcceleratorW (USER32.@)
5302 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5304 /* YES, Accel16! */
5305 LPACCEL16 lpAccelTbl;
5306 int i;
5308 if (!hWnd || !msg) return 0;
5310 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5312 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5313 return 0;
5316 switch (msg->message)
5318 case WM_KEYDOWN:
5319 case WM_SYSKEYDOWN:
5320 case WM_CHAR:
5321 case WM_SYSCHAR:
5322 break;
5324 default:
5325 return 0;
5328 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5329 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5330 i = 0;
5333 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5334 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5335 return 1;
5336 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5338 return 0;