user32: show menus with WS_EX_TOPMOST exStyle
[wine/kumbayo.git] / dlls / user32 / menu.c
blob3feccb05c8c9cb01ac7d9bd5e3824edaa4802fb1
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
56 #include "wownt32.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "win.h"
60 #include "controls.h"
61 #include "user_private.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(menu);
65 WINE_DECLARE_DEBUG_CHANNEL(accel);
67 /* internal popup menu window messages */
69 #define MM_SETMENUHANDLE (WM_USER + 0)
70 #define MM_GETMENUHANDLE (WM_USER + 1)
72 /* Menu item structure */
73 typedef struct {
74 /* ----------- MENUITEMINFO Stuff ----------- */
75 UINT fType; /* Item type. */
76 UINT fState; /* Item state. */
77 UINT_PTR wID; /* Item id. */
78 HMENU hSubMenu; /* Pop-up menu. */
79 HBITMAP hCheckBit; /* Bitmap when checked. */
80 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
81 LPWSTR text; /* Item text. */
82 ULONG_PTR dwItemData; /* Application defined. */
83 LPWSTR dwTypeData; /* depends on fMask */
84 HBITMAP hbmpItem; /* bitmap */
85 /* ----------- Wine stuff ----------- */
86 RECT rect; /* Item area (relative to menu window) */
87 UINT xTab; /* X position of text after Tab */
88 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
89 * bitmap */
90 } MENUITEM;
92 /* Popup menu structure */
93 typedef struct {
94 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
95 WORD wMagic; /* Magic number */
96 WORD Width; /* Width of the whole menu */
97 WORD Height; /* Height of the whole menu */
98 UINT nItems; /* Number of items in the menu */
99 HWND hWnd; /* Window containing the menu */
100 MENUITEM *items; /* Array of menu items */
101 UINT FocusedItem; /* Currently focused item */
102 HWND hwndOwner; /* window receiving the messages for ownerdraw */
103 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
104 BOOL bScrolling; /* Scroll arrows are active */
105 UINT nScrollPos; /* Current scroll position */
106 UINT nTotalHeight; /* Total height of menu items inside menu */
107 /* ------------ MENUINFO members ------ */
108 DWORD dwStyle; /* Extended menu style */
109 UINT cyMax; /* max height of the whole menu, 0 is screen height */
110 HBRUSH hbrBack; /* brush for menu background */
111 DWORD dwContextHelpID;
112 DWORD dwMenuData; /* application defined value */
113 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
114 SIZE maxBmpSize; /* Maximum size of the bitmap items */
115 } POPUPMENU, *LPPOPUPMENU;
117 /* internal flags for menu tracking */
119 #define TF_ENDMENU 0x0001
120 #define TF_SUSPENDPOPUP 0x0002
121 #define TF_SKIPREMOVE 0x0004
123 typedef struct
125 UINT trackFlags;
126 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
127 HMENU hTopMenu; /* initial menu */
128 HWND hOwnerWnd; /* where notifications are sent */
129 POINT pt;
130 } MTRACKER;
132 #define MENU_MAGIC 0x554d /* 'MU' */
134 #define ITEM_PREV -1
135 #define ITEM_NEXT 1
137 /* Internal MENU_TrackMenu() flags */
138 #define TPM_INTERNAL 0xF0000000
139 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
140 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
141 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
143 /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
146 /* top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
150 /* (other menu->FocusedItem values give the position of the focused item) */
151 #define NO_SELECTED_ITEM 0xffff
153 #define MENU_ITEM_TYPE(flags) \
154 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
156 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
157 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
158 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
160 #define IS_SYSTEM_MENU(menu) \
161 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
163 #define MENUITEMINFO_TYPE_MASK \
164 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
165 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
166 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
167 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
168 #define STATE_MASK (~TYPE_MASK)
169 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
171 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
173 static SIZE menucharsize;
174 static UINT ODitemheight; /* default owner drawn item height */
176 /* Use global popup window because there's no way 2 menus can
177 * be tracked at the same time. */
178 static HWND top_popup;
180 /* Flag set by EndMenu() to force an exit from menu tracking */
181 static BOOL fEndMenu = FALSE;
183 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
185 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
187 /*********************************************************************
188 * menu class descriptor
190 const struct builtin_class_descr MENU_builtin_class =
192 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
193 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
194 NULL, /* procA (winproc is Unicode only) */
195 PopupMenuWndProc, /* procW */
196 sizeof(HMENU), /* extra */
197 IDC_ARROW, /* cursor */
198 (HBRUSH)(COLOR_MENU+1) /* brush */
202 /***********************************************************************
203 * debug_print_menuitem
205 * Print a menuitem in readable form.
208 #define debug_print_menuitem(pre, mp, post) \
209 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
211 #define MENUOUT(text) \
212 TRACE("%s%s", (count++ ? "," : ""), (text))
214 #define MENUFLAG(bit,text) \
215 do { \
216 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
217 } while (0)
219 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
220 const char *postfix)
222 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
223 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
224 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
225 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
226 TRACE("%s ", prefix);
227 if (mp) {
228 UINT flags = mp->fType;
229 TRACE( "{ ID=0x%lx", mp->wID);
230 if ( mp->hSubMenu)
231 TRACE( ", Sub=%p", mp->hSubMenu);
232 if (flags) {
233 int count = 0;
234 TRACE( ", fType=");
235 MENUFLAG( MFT_SEPARATOR, "sep");
236 MENUFLAG( MFT_OWNERDRAW, "own");
237 MENUFLAG( MFT_BITMAP, "bit");
238 MENUFLAG(MF_POPUP, "pop");
239 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240 MENUFLAG(MFT_MENUBREAK, "brk");
241 MENUFLAG(MFT_RADIOCHECK, "radio");
242 MENUFLAG(MFT_RIGHTORDER, "rorder");
243 MENUFLAG(MF_SYSMENU, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
245 if (flags)
246 TRACE( "+0x%x", flags);
248 flags = mp->fState;
249 if (flags) {
250 int count = 0;
251 TRACE( ", State=");
252 MENUFLAG(MFS_GRAYED, "grey");
253 MENUFLAG(MFS_DEFAULT, "default");
254 MENUFLAG(MFS_DISABLED, "dis");
255 MENUFLAG(MFS_CHECKED, "check");
256 MENUFLAG(MFS_HILITE, "hi");
257 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
258 MENUFLAG(MF_MOUSESELECT, "mouse");
259 if (flags)
260 TRACE( "+0x%x", flags);
262 if (mp->hCheckBit)
263 TRACE( ", Chk=%p", mp->hCheckBit);
264 if (mp->hUnCheckBit)
265 TRACE( ", Unc=%p", mp->hUnCheckBit);
266 if (mp->text)
267 TRACE( ", Text=%s", debugstr_w(mp->text));
268 if (mp->dwItemData)
269 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
270 if (mp->hbmpItem)
272 if( IS_MAGIC_BITMAP(mp->hbmpItem))
273 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
274 else
275 TRACE( ", hbitmap=%p", mp->hbmpItem);
277 TRACE( " }");
278 } else
279 TRACE( "NULL");
280 TRACE(" %s\n", postfix);
283 #undef MENUOUT
284 #undef MENUFLAG
287 /***********************************************************************
288 * MENU_GetMenu
290 * Validate the given menu handle and returns the menu structure pointer.
292 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
294 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
295 if (!menu || menu->wMagic != MENU_MAGIC)
297 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
298 menu = NULL;
300 return menu;
303 /***********************************************************************
304 * get_win_sys_menu
306 * Get the system menu of a window
308 static HMENU get_win_sys_menu( HWND hwnd )
310 HMENU ret = 0;
311 WND *win = WIN_GetPtr( hwnd );
312 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
314 ret = win->hSysMenu;
315 WIN_ReleasePtr( win );
317 return ret;
320 /***********************************************************************
321 * get_menu_font
323 static HFONT get_menu_font( BOOL bold )
325 static HFONT hMenuFont, hMenuFontBold;
327 HFONT ret = bold ? hMenuFontBold : hMenuFont;
329 if (!ret)
331 NONCLIENTMETRICSW ncm;
332 HFONT prev;
334 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
335 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
337 if (bold)
339 ncm.lfMenuFont.lfWeight += 300;
340 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
342 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
343 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
344 ret, NULL );
345 if (prev)
347 /* another thread beat us to it */
348 DeleteObject( ret );
349 ret = prev;
352 return ret;
355 /***********************************************************************
356 * get_arrow_bitmap
358 static HBITMAP get_arrow_bitmap(void)
360 static HBITMAP arrow_bitmap;
362 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
363 return arrow_bitmap;
366 /***********************************************************************
367 * get_down_arrow_bitmap
369 static HBITMAP get_down_arrow_bitmap(void)
371 static HBITMAP arrow_bitmap;
373 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
374 return arrow_bitmap;
377 /***********************************************************************
378 * get_down_arrow_inactive_bitmap
380 static HBITMAP get_down_arrow_inactive_bitmap(void)
382 static HBITMAP arrow_bitmap;
384 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
385 return arrow_bitmap;
388 /***********************************************************************
389 * get_up_arrow_bitmap
391 static HBITMAP get_up_arrow_bitmap(void)
393 static HBITMAP arrow_bitmap;
395 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
396 return arrow_bitmap;
399 /***********************************************************************
400 * get_up_arrow_inactive_bitmap
402 static HBITMAP get_up_arrow_inactive_bitmap(void)
404 static HBITMAP arrow_bitmap;
406 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
407 return arrow_bitmap;
410 /***********************************************************************
411 * MENU_CopySysPopup
413 * Return the default system menu.
415 static HMENU MENU_CopySysPopup(void)
417 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
418 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
420 if( hMenu ) {
421 POPUPMENU* menu = MENU_GetMenu(hMenu);
422 menu->wFlags |= MF_SYSMENU | MF_POPUP;
423 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
425 else
426 ERR("Unable to load default system menu\n" );
428 TRACE("returning %p.\n", hMenu );
430 return hMenu;
434 /**********************************************************************
435 * MENU_GetSysMenu
437 * Create a copy of the system menu. System menu in Windows is
438 * a special menu bar with the single entry - system menu popup.
439 * This popup is presented to the outside world as a "system menu".
440 * However, the real system menu handle is sometimes seen in the
441 * WM_MENUSELECT parameters (and Word 6 likes it this way).
443 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
445 HMENU hMenu;
447 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
448 if ((hMenu = CreateMenu()))
450 POPUPMENU *menu = MENU_GetMenu(hMenu);
451 menu->wFlags = MF_SYSMENU;
452 menu->hWnd = WIN_GetFullHandle( hWnd );
453 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
455 if (!hPopupMenu)
456 hPopupMenu = MENU_CopySysPopup();
458 if (hPopupMenu)
460 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
461 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
463 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
464 (UINT_PTR)hPopupMenu, NULL );
466 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
467 menu->items[0].fState = 0;
468 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
470 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
471 return hMenu;
473 DestroyMenu( hMenu );
475 ERR("failed to load system menu!\n");
476 return 0;
480 /***********************************************************************
481 * MENU_InitSysMenuPopup
483 * Grey the appropriate items in System menu.
485 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
487 BOOL gray;
489 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
490 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
491 gray = ((style & WS_MAXIMIZE) != 0);
492 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
493 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
494 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
496 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
497 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
498 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
499 gray = (clsStyle & CS_NOCLOSE) != 0;
501 /* The menu item must keep its state if it's disabled */
502 if(gray)
503 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
507 /******************************************************************************
509 * UINT MENU_GetStartOfNextColumn(
510 * HMENU hMenu )
512 *****************************************************************************/
514 static UINT MENU_GetStartOfNextColumn(
515 HMENU hMenu )
517 POPUPMENU *menu = MENU_GetMenu(hMenu);
518 UINT i;
520 if(!menu)
521 return NO_SELECTED_ITEM;
523 i = menu->FocusedItem + 1;
524 if( i == NO_SELECTED_ITEM )
525 return i;
527 for( ; i < menu->nItems; ++i ) {
528 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
529 return i;
532 return NO_SELECTED_ITEM;
536 /******************************************************************************
538 * UINT MENU_GetStartOfPrevColumn(
539 * HMENU hMenu )
541 *****************************************************************************/
543 static UINT MENU_GetStartOfPrevColumn(
544 HMENU hMenu )
546 POPUPMENU *menu = MENU_GetMenu(hMenu);
547 UINT i;
549 if( !menu )
550 return NO_SELECTED_ITEM;
552 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
553 return NO_SELECTED_ITEM;
555 /* Find the start of the column */
557 for(i = menu->FocusedItem; i != 0 &&
558 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
559 --i); /* empty */
561 if(i == 0)
562 return NO_SELECTED_ITEM;
564 for(--i; i != 0; --i) {
565 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
566 break;
569 TRACE("ret %d.\n", i );
571 return i;
576 /***********************************************************************
577 * MENU_FindItem
579 * Find a menu item. Return a pointer on the item, and modifies *hmenu
580 * in case the item was in a sub-menu.
582 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
584 POPUPMENU *menu;
585 MENUITEM *fallback = NULL;
586 UINT fallback_pos = 0;
587 UINT i;
589 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
590 if (wFlags & MF_BYPOSITION)
592 if (*nPos >= menu->nItems) return NULL;
593 return &menu->items[*nPos];
595 else
597 MENUITEM *item = menu->items;
598 for (i = 0; i < menu->nItems; i++, item++)
600 if (item->fType & MF_POPUP)
602 HMENU hsubmenu = item->hSubMenu;
603 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
604 if (subitem)
606 *hmenu = hsubmenu;
607 return subitem;
609 else if (item->wID == *nPos)
611 /* fallback to this item if nothing else found */
612 fallback_pos = i;
613 fallback = item;
616 else if (item->wID == *nPos)
618 *nPos = i;
619 return item;
624 if (fallback)
625 *nPos = fallback_pos;
627 return fallback;
630 /***********************************************************************
631 * MENU_FindSubMenu
633 * Find a Sub menu. Return the position of the submenu, and modifies
634 * *hmenu in case it is found in another sub-menu.
635 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
637 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
639 POPUPMENU *menu;
640 UINT i;
641 MENUITEM *item;
642 if (((*hmenu)==(HMENU)0xffff) ||
643 (!(menu = MENU_GetMenu(*hmenu))))
644 return NO_SELECTED_ITEM;
645 item = menu->items;
646 for (i = 0; i < menu->nItems; i++, item++) {
647 if(!(item->fType & MF_POPUP)) continue;
648 if (item->hSubMenu == hSubTarget) {
649 return i;
651 else {
652 HMENU hsubmenu = item->hSubMenu;
653 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
654 if (pos != NO_SELECTED_ITEM) {
655 *hmenu = hsubmenu;
656 return pos;
660 return NO_SELECTED_ITEM;
663 /***********************************************************************
664 * MENU_FreeItemData
666 static void MENU_FreeItemData( MENUITEM* item )
668 /* delete text */
669 HeapFree( GetProcessHeap(), 0, item->text );
672 /***********************************************************************
673 * MENU_AdjustMenuItemRect
675 * Adjust menu item rectangle according to scrolling state.
677 static void
678 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
680 if (menu->bScrolling)
682 UINT arrow_bitmap_width, arrow_bitmap_height;
683 BITMAP bmp;
685 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
686 arrow_bitmap_width = bmp.bmWidth;
687 arrow_bitmap_height = bmp.bmHeight;
688 rect->top += arrow_bitmap_height - menu->nScrollPos;
689 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
694 /***********************************************************************
695 * MENU_FindItemByCoords
697 * Find the item at the specified coordinates (screen coords). Does
698 * not work for child windows and therefore should not be called for
699 * an arbitrary system menu.
701 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
702 POINT pt, UINT *pos )
704 MENUITEM *item;
705 UINT i;
706 RECT rect;
708 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
709 pt.x -= rect.left;
710 pt.y -= rect.top;
711 item = menu->items;
712 for (i = 0; i < menu->nItems; i++, item++)
714 rect = item->rect;
715 MENU_AdjustMenuItemRect(menu, &rect);
716 if (PtInRect(&rect, pt))
718 if (pos) *pos = i;
719 return item;
722 return NULL;
726 /***********************************************************************
727 * MENU_FindItemByKey
729 * Find the menu item selected by a key press.
730 * Return item id, -1 if none, -2 if we should close the menu.
732 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
733 WCHAR key, BOOL forceMenuChar )
735 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
737 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
739 if (hmenu)
741 POPUPMENU *menu = MENU_GetMenu( hmenu );
742 MENUITEM *item = menu->items;
743 LRESULT menuchar;
745 if( !forceMenuChar )
747 UINT i;
749 for (i = 0; i < menu->nItems; i++, item++)
751 if( item->text)
753 WCHAR *p = item->text - 2;
756 p = strchrW (p + 2, '&');
758 while (p != NULL && p [1] == '&');
759 if (p && (toupperW(p[1]) == toupperW(key))) return i;
763 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
764 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
765 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
766 if (HIWORD(menuchar) == 1) return (UINT)(-2);
768 return (UINT)(-1);
772 /***********************************************************************
773 * MENU_GetBitmapItemSize
775 * Get the size of a bitmap item.
777 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
778 HWND hwndOwner)
780 BITMAP bm;
781 HBITMAP bmp = lpitem->hbmpItem;
783 size->cx = size->cy = 0;
785 /* check if there is a magic menu item associated with this item */
786 switch( (INT_PTR) bmp )
788 case (INT_PTR)HBMMENU_CALLBACK:
790 MEASUREITEMSTRUCT measItem;
791 measItem.CtlType = ODT_MENU;
792 measItem.CtlID = 0;
793 measItem.itemID = lpitem->wID;
794 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
795 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
796 measItem.itemData = lpitem->dwItemData;
797 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
798 size->cx = measItem.itemWidth;
799 size->cy = measItem.itemHeight;
800 return;
802 break;
803 case (INT_PTR)HBMMENU_SYSTEM:
804 if (lpitem->dwItemData)
806 bmp = (HBITMAP)lpitem->dwItemData;
807 break;
809 /* fall through */
810 case (INT_PTR)HBMMENU_MBAR_RESTORE:
811 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
812 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
813 case (INT_PTR)HBMMENU_MBAR_CLOSE:
814 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
815 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
816 size->cy = size->cx;
817 return;
818 case (INT_PTR)HBMMENU_POPUP_CLOSE:
819 case (INT_PTR)HBMMENU_POPUP_RESTORE:
820 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
821 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
822 FIXME("Magic %p not implemented\n", bmp );
823 return;
825 if (GetObjectW(bmp, sizeof(bm), &bm ))
827 size->cx = bm.bmWidth;
828 size->cy = bm.bmHeight;
832 /***********************************************************************
833 * MENU_DrawBitmapItem
835 * Draw a bitmap item.
837 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
838 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
840 BITMAP bm;
841 DWORD rop;
842 HDC hdcMem;
843 HBITMAP bmp;
844 int w = rect->right - rect->left;
845 int h = rect->bottom - rect->top;
846 int bmp_xoffset = 0;
847 int left, top;
848 HBITMAP hbmToDraw = lpitem->hbmpItem;
849 bmp = hbmToDraw;
851 /* Check if there is a magic menu item associated with this item */
852 if (IS_MAGIC_BITMAP(hbmToDraw))
854 UINT flags = 0;
855 RECT r;
857 switch((INT_PTR)hbmToDraw)
859 case (INT_PTR)HBMMENU_SYSTEM:
860 if (lpitem->dwItemData)
862 bmp = (HBITMAP)lpitem->dwItemData;
863 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
865 else
867 static HBITMAP hBmpSysMenu;
869 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
870 bmp = hBmpSysMenu;
871 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
872 /* only use right half of the bitmap */
873 bmp_xoffset = bm.bmWidth / 2;
874 bm.bmWidth -= bmp_xoffset;
876 goto got_bitmap;
877 case (INT_PTR)HBMMENU_MBAR_RESTORE:
878 flags = DFCS_CAPTIONRESTORE;
879 break;
880 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
881 flags = DFCS_CAPTIONMIN;
882 break;
883 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
884 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
885 break;
886 case (INT_PTR)HBMMENU_MBAR_CLOSE:
887 flags = DFCS_CAPTIONCLOSE;
888 break;
889 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
890 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
891 break;
892 case (INT_PTR)HBMMENU_CALLBACK:
894 DRAWITEMSTRUCT drawItem;
895 drawItem.CtlType = ODT_MENU;
896 drawItem.CtlID = 0;
897 drawItem.itemID = lpitem->wID;
898 drawItem.itemAction = odaction;
899 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
900 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
901 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
902 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
903 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
904 drawItem.hwndItem = (HWND)hmenu;
905 drawItem.hDC = hdc;
906 drawItem.itemData = lpitem->dwItemData;
907 drawItem.rcItem = *rect;
908 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
909 return;
911 break;
912 case (INT_PTR)HBMMENU_POPUP_CLOSE:
913 case (INT_PTR)HBMMENU_POPUP_RESTORE:
914 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
915 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
916 default:
917 FIXME("Magic %p not implemented\n", hbmToDraw);
918 return;
920 r = *rect;
921 InflateRect( &r, -1, -1 );
922 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
923 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
924 return;
927 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
929 got_bitmap:
930 hdcMem = CreateCompatibleDC( hdc );
931 SelectObject( hdcMem, bmp );
933 /* handle fontsize > bitmap_height */
934 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
935 left=rect->left;
936 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
937 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
938 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
939 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
940 DeleteDC( hdcMem );
944 /***********************************************************************
945 * MENU_CalcItemSize
947 * Calculate the size of the menu item and store it in lpitem->rect.
949 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
950 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
952 WCHAR *p;
953 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
954 UINT arrow_bitmap_width;
955 BITMAP bm;
956 INT itemheight;
958 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
959 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
960 (menuBar ? " (MenuBar)" : ""));
962 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
963 arrow_bitmap_width = bm.bmWidth;
965 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
966 if( !menucharsize.cx ) {
967 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
968 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
969 * but it is unlikely an application will depend on that */
970 ODitemheight = HIWORD( GetDialogBaseUnits());
973 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
975 if (lpitem->fType & MF_OWNERDRAW)
977 MEASUREITEMSTRUCT mis;
978 mis.CtlType = ODT_MENU;
979 mis.CtlID = 0;
980 mis.itemID = lpitem->wID;
981 mis.itemData = lpitem->dwItemData;
982 mis.itemHeight = ODitemheight;
983 mis.itemWidth = 0;
984 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
985 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
986 * width of a menufont character to the width of an owner-drawn menu.
988 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
989 if (menuBar) {
990 /* under at least win95 you seem to be given a standard
991 height for the menu and the height value is ignored */
992 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
993 } else
994 lpitem->rect.bottom += mis.itemHeight;
996 TRACE("id=%04lx size=%dx%d\n",
997 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
998 lpitem->rect.bottom-lpitem->rect.top);
999 return;
1002 if (lpitem->fType & MF_SEPARATOR)
1004 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1005 if( !menuBar)
1006 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1007 return;
1010 itemheight = 0;
1011 lpitem->xTab = 0;
1013 if (!menuBar) {
1014 if (lpitem->hbmpItem) {
1015 SIZE size;
1017 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1018 /* Keep the size of the bitmap in callback mode to be able
1019 * to draw it correctly */
1020 lpitem->bmpsize = size;
1021 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1022 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1023 lpitem->rect.right += size.cx + 2;
1024 itemheight = size.cy + 2;
1026 if( !(lppop->dwStyle & MNS_NOCHECK))
1027 lpitem->rect.right += check_bitmap_width;
1028 lpitem->rect.right += 4 + menucharsize.cx;
1029 lpitem->xTab = lpitem->rect.right;
1030 lpitem->rect.right += arrow_bitmap_width;
1031 } else if (lpitem->hbmpItem) { /* menuBar */
1032 SIZE size;
1034 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1035 lpitem->bmpsize = size;
1036 lpitem->rect.right += size.cx;
1037 if( lpitem->text) lpitem->rect.right += 2;
1038 itemheight = size.cy;
1041 /* it must be a text item - unless it's the system menu */
1042 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1043 HFONT hfontOld = NULL;
1044 RECT rc = lpitem->rect;
1045 LONG txtheight, txtwidth;
1047 if ( lpitem->fState & MFS_DEFAULT ) {
1048 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1050 if (menuBar) {
1051 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1052 DT_SINGLELINE|DT_CALCRECT);
1053 lpitem->rect.right += rc.right - rc.left;
1054 itemheight = max( max( itemheight, txtheight),
1055 GetSystemMetrics( SM_CYMENU) - 1);
1056 lpitem->rect.right += 2 * menucharsize.cx;
1057 } else {
1058 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1059 RECT tmprc = rc;
1060 LONG tmpheight;
1061 int n = (int)( p - lpitem->text);
1062 /* Item contains a tab (only meaningful in popup menus) */
1063 /* get text size before the tab */
1064 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1065 DT_SINGLELINE|DT_CALCRECT);
1066 txtwidth = rc.right - rc.left;
1067 p += 1; /* advance past the Tab */
1068 /* get text size after the tab */
1069 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1070 DT_SINGLELINE|DT_CALCRECT);
1071 lpitem->xTab += txtwidth;
1072 txtheight = max( txtheight, tmpheight);
1073 txtwidth += menucharsize.cx + /* space for the tab */
1074 tmprc.right - tmprc.left; /* space for the short cut */
1075 } else {
1076 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1077 DT_SINGLELINE|DT_CALCRECT);
1078 txtwidth = rc.right - rc.left;
1079 lpitem->xTab += txtwidth;
1081 lpitem->rect.right += 2 + txtwidth;
1082 itemheight = max( itemheight,
1083 max( txtheight + 2, menucharsize.cy + 4));
1085 if (hfontOld) SelectObject (hdc, hfontOld);
1086 } else if( menuBar) {
1087 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1089 lpitem->rect.bottom += itemheight;
1090 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1094 /***********************************************************************
1095 * MENU_GetMaxPopupHeight
1097 static UINT
1098 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1100 if (lppop->cyMax)
1101 return lppop->cyMax;
1102 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1106 /***********************************************************************
1107 * MENU_PopupMenuCalcSize
1109 * Calculate the size of a popup menu.
1111 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1113 MENUITEM *lpitem;
1114 HDC hdc;
1115 int start, i;
1116 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1118 lppop->Width = lppop->Height = 0;
1119 if (lppop->nItems == 0) return;
1120 hdc = GetDC( 0 );
1122 SelectObject( hdc, get_menu_font(FALSE));
1124 start = 0;
1125 maxX = 2 + 1;
1127 lppop->maxBmpSize.cx = 0;
1128 lppop->maxBmpSize.cy = 0;
1130 while (start < lppop->nItems)
1132 lpitem = &lppop->items[start];
1133 orgX = maxX;
1134 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1135 orgX += MENU_COL_SPACE;
1136 orgY = MENU_TOP_MARGIN;
1138 maxTab = maxTabWidth = 0;
1139 /* Parse items until column break or end of menu */
1140 for (i = start; i < lppop->nItems; i++, lpitem++)
1142 if ((i != start) &&
1143 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1145 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1146 maxX = max( maxX, lpitem->rect.right );
1147 orgY = lpitem->rect.bottom;
1148 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1150 maxTab = max( maxTab, lpitem->xTab );
1151 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1155 /* Finish the column (set all items to the largest width found) */
1156 maxX = max( maxX, maxTab + maxTabWidth );
1157 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1159 lpitem->rect.right = maxX;
1160 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1161 lpitem->xTab = maxTab;
1164 lppop->Height = max( lppop->Height, orgY );
1167 lppop->Width = maxX;
1169 /* space for 3d border */
1170 lppop->Height += MENU_BOTTOM_MARGIN;
1171 lppop->Width += 2;
1173 /* Adjust popup height if it exceeds maximum */
1174 maxHeight = MENU_GetMaxPopupHeight(lppop);
1175 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1176 if (lppop->Height >= maxHeight)
1178 lppop->Height = maxHeight;
1179 lppop->bScrolling = TRUE;
1181 else
1183 lppop->bScrolling = FALSE;
1186 ReleaseDC( 0, hdc );
1190 /***********************************************************************
1191 * MENU_MenuBarCalcSize
1193 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1194 * height is off by 1 pixel which causes lengthy window relocations when
1195 * active document window is maximized/restored.
1197 * Calculate the size of the menu bar.
1199 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1200 LPPOPUPMENU lppop, HWND hwndOwner )
1202 MENUITEM *lpitem;
1203 int start, i, orgX, orgY, maxY, helpPos;
1205 if ((lprect == NULL) || (lppop == NULL)) return;
1206 if (lppop->nItems == 0) return;
1207 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1208 lppop->Width = lprect->right - lprect->left;
1209 lppop->Height = 0;
1210 maxY = lprect->top+1;
1211 start = 0;
1212 helpPos = -1;
1213 lppop->maxBmpSize.cx = 0;
1214 lppop->maxBmpSize.cy = 0;
1215 while (start < lppop->nItems)
1217 lpitem = &lppop->items[start];
1218 orgX = lprect->left;
1219 orgY = maxY;
1221 /* Parse items until line break or end of menu */
1222 for (i = start; i < lppop->nItems; i++, lpitem++)
1224 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1225 if ((i != start) &&
1226 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1228 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1229 debug_print_menuitem (" item: ", lpitem, "");
1230 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1232 if (lpitem->rect.right > lprect->right)
1234 if (i != start) break;
1235 else lpitem->rect.right = lprect->right;
1237 maxY = max( maxY, lpitem->rect.bottom );
1238 orgX = lpitem->rect.right;
1241 /* Finish the line (set all items to the largest height found) */
1242 while (start < i) lppop->items[start++].rect.bottom = maxY;
1245 lprect->bottom = maxY;
1246 lppop->Height = lprect->bottom - lprect->top;
1248 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1249 /* the last item (if several lines, only move the last line) */
1250 lpitem = &lppop->items[lppop->nItems-1];
1251 orgY = lpitem->rect.top;
1252 orgX = lprect->right;
1253 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1254 if ( (helpPos==-1) || (helpPos>i) )
1255 break; /* done */
1256 if (lpitem->rect.top != orgY) break; /* Other line */
1257 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1258 lpitem->rect.left += orgX - lpitem->rect.right;
1259 lpitem->rect.right = orgX;
1260 orgX = lpitem->rect.left;
1265 /***********************************************************************
1266 * MENU_DrawScrollArrows
1268 * Draw scroll arrows.
1270 static void
1271 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1273 HDC hdcMem = CreateCompatibleDC(hdc);
1274 HBITMAP hOrigBitmap;
1275 UINT arrow_bitmap_width, arrow_bitmap_height;
1276 BITMAP bmp;
1277 RECT rect;
1279 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1280 arrow_bitmap_width = bmp.bmWidth;
1281 arrow_bitmap_height = bmp.bmHeight;
1284 if (lppop->nScrollPos)
1285 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1286 else
1287 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1288 rect.left = 0;
1289 rect.top = 0;
1290 rect.right = lppop->Width;
1291 rect.bottom = arrow_bitmap_height;
1292 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1293 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1294 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1295 rect.top = lppop->Height - arrow_bitmap_height;
1296 rect.bottom = lppop->Height;
1297 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1298 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1299 SelectObject(hdcMem, get_down_arrow_bitmap());
1300 else
1301 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1302 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1303 lppop->Height - arrow_bitmap_height,
1304 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1305 SelectObject(hdcMem, hOrigBitmap);
1306 DeleteDC(hdcMem);
1310 /***********************************************************************
1311 * draw_popup_arrow
1313 * Draws the popup-menu arrow.
1315 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1316 UINT arrow_bitmap_height)
1318 HDC hdcMem = CreateCompatibleDC( hdc );
1319 HBITMAP hOrigBitmap;
1321 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1322 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1323 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1324 arrow_bitmap_width, arrow_bitmap_height,
1325 hdcMem, 0, 0, SRCCOPY );
1326 SelectObject( hdcMem, hOrigBitmap );
1327 DeleteDC( hdcMem );
1329 /***********************************************************************
1330 * MENU_DrawMenuItem
1332 * Draw a single menu item.
1334 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1335 UINT height, BOOL menuBar, UINT odaction )
1337 RECT rect;
1338 BOOL flat_menu = FALSE;
1339 int bkgnd;
1340 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1341 POPUPMENU *menu = MENU_GetMenu(hmenu);
1342 RECT bmprc;
1344 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1346 if (!menuBar) {
1347 BITMAP bmp;
1348 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1349 arrow_bitmap_width = bmp.bmWidth;
1350 arrow_bitmap_height = bmp.bmHeight;
1353 if (lpitem->fType & MF_SYSMENU)
1355 if( !IsIconic(hwnd) )
1356 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1357 return;
1360 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1361 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1363 /* Setup colors */
1365 if (lpitem->fState & MF_HILITE)
1367 if(menuBar && !flat_menu) {
1368 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1369 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1370 } else {
1371 if(lpitem->fState & MF_GRAYED)
1372 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1373 else
1374 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1375 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1378 else
1380 if (lpitem->fState & MF_GRAYED)
1381 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1382 else
1383 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1384 SetBkColor( hdc, GetSysColor( bkgnd ) );
1387 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1388 rect = lpitem->rect;
1389 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1391 if (lpitem->fType & MF_OWNERDRAW)
1394 ** Experimentation under Windows reveals that an owner-drawn
1395 ** menu is given the rectangle which includes the space it requested
1396 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1397 ** and a popup-menu arrow. This is the value of lpitem->rect.
1398 ** Windows will leave all drawing to the application except for
1399 ** the popup-menu arrow. Windows always draws that itself, after
1400 ** the menu owner has finished drawing.
1402 DRAWITEMSTRUCT dis;
1404 dis.CtlType = ODT_MENU;
1405 dis.CtlID = 0;
1406 dis.itemID = lpitem->wID;
1407 dis.itemData = lpitem->dwItemData;
1408 dis.itemState = 0;
1409 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1410 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1411 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1412 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1413 dis.hwndItem = (HWND)hmenu;
1414 dis.hDC = hdc;
1415 dis.rcItem = rect;
1416 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1417 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1418 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1419 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1420 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1421 /* Draw the popup-menu arrow */
1422 if (lpitem->fType & MF_POPUP)
1423 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1424 arrow_bitmap_height);
1425 return;
1428 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1430 if (lpitem->fState & MF_HILITE)
1432 if (flat_menu)
1434 InflateRect (&rect, -1, -1);
1435 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1436 InflateRect (&rect, 1, 1);
1437 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1439 else
1441 if(menuBar)
1442 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1443 else
1444 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1447 else
1448 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1450 SetBkMode( hdc, TRANSPARENT );
1452 /* vertical separator */
1453 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1455 HPEN oldPen;
1456 RECT rc = rect;
1458 rc.left -= MENU_COL_SPACE / 2 + 1;
1459 rc.top = 3;
1460 rc.bottom = height - 3;
1461 if (flat_menu)
1463 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1464 MoveToEx( hdc, rc.left, rc.top, NULL );
1465 LineTo( hdc, rc.left, rc.bottom );
1466 SelectObject( hdc, oldPen );
1468 else
1469 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1472 /* horizontal separator */
1473 if (lpitem->fType & MF_SEPARATOR)
1475 HPEN oldPen;
1476 RECT rc = rect;
1478 rc.left++;
1479 rc.right--;
1480 rc.top = ( rc.top + rc.bottom) / 2;
1481 if (flat_menu)
1483 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1484 MoveToEx( hdc, rc.left, rc.top, NULL );
1485 LineTo( hdc, rc.right, rc.top );
1486 SelectObject( hdc, oldPen );
1488 else
1489 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1490 return;
1493 /* helper lines for debugging */
1494 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1495 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1496 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1497 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1500 if (lpitem->hbmpItem) {
1501 /* calculate the bitmap rectangle in coordinates relative
1502 * to the item rectangle */
1503 if( menuBar) {
1504 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1505 bmprc.left = 3;
1506 else
1507 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1508 } else {
1509 bmprc.left = 4;
1510 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1511 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1513 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1514 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1515 bmprc.top = 0;
1516 else
1517 bmprc.top = (rect.bottom - rect.top -
1518 lpitem->bmpsize.cy) / 2;
1519 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1522 if (!menuBar)
1524 HBITMAP bm;
1525 INT y = rect.top + rect.bottom;
1526 RECT rc = rect;
1527 int checked = FALSE;
1528 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1529 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1530 /* Draw the check mark
1532 * FIXME:
1533 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1535 if( !(menu->dwStyle & MNS_NOCHECK)) {
1536 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1537 lpitem->hUnCheckBit;
1538 if (bm) /* we have a custom bitmap */
1540 HDC hdcMem = CreateCompatibleDC( hdc );
1542 SelectObject( hdcMem, bm );
1543 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1544 check_bitmap_width, check_bitmap_height,
1545 hdcMem, 0, 0, SRCCOPY );
1546 DeleteDC( hdcMem );
1547 checked = TRUE;
1549 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1551 RECT r;
1552 HBITMAP bm = CreateBitmap( check_bitmap_width,
1553 check_bitmap_height, 1, 1, NULL );
1554 HDC hdcMem = CreateCompatibleDC( hdc );
1556 SelectObject( hdcMem, bm );
1557 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1558 DrawFrameControl( hdcMem, &r, DFC_MENU,
1559 (lpitem->fType & MFT_RADIOCHECK) ?
1560 DFCS_MENUBULLET : DFCS_MENUCHECK );
1561 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1562 hdcMem, 0, 0, SRCCOPY );
1563 DeleteDC( hdcMem );
1564 DeleteObject( bm );
1565 checked = TRUE;
1568 if( lpitem->hbmpItem &&
1569 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1570 POINT origorg;
1571 /* some applications make this assumption on the DC's origin */
1572 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1573 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1574 odaction, FALSE);
1575 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1577 /* Draw the popup-menu arrow */
1578 if (lpitem->fType & MF_POPUP)
1579 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1580 arrow_bitmap_height);
1581 rect.left += 4;
1582 if( !(menu->dwStyle & MNS_NOCHECK))
1583 rect.left += check_bitmap_width;
1584 rect.right -= arrow_bitmap_width;
1586 else if( lpitem->hbmpItem)
1587 { /* Draw the bitmap */
1588 POINT origorg;
1590 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1591 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1592 odaction, menuBar);
1593 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1595 /* process text if present */
1596 if (lpitem->text)
1598 register int i;
1599 HFONT hfontOld = 0;
1601 UINT uFormat = (menuBar) ?
1602 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1603 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1605 if( !(menu->dwStyle & MNS_CHECKORBMP))
1606 rect.left += menu->maxBmpSize.cx;
1608 if ( lpitem->fState & MFS_DEFAULT )
1610 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1613 if (menuBar) {
1614 if( lpitem->hbmpItem)
1615 rect.left += lpitem->bmpsize.cx;
1616 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1617 rect.left += menucharsize.cx;
1618 rect.right -= menucharsize.cx;
1621 for (i = 0; lpitem->text[i]; i++)
1622 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1623 break;
1625 if(lpitem->fState & MF_GRAYED)
1627 if (!(lpitem->fState & MF_HILITE) )
1629 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1630 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1631 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1632 --rect.left; --rect.top; --rect.right; --rect.bottom;
1634 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1637 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1639 /* paint the shortcut text */
1640 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1642 if (lpitem->text[i] == '\t')
1644 rect.left = lpitem->xTab;
1645 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1647 else
1649 rect.right = lpitem->xTab;
1650 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1653 if(lpitem->fState & MF_GRAYED)
1655 if (!(lpitem->fState & MF_HILITE) )
1657 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1658 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1659 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1660 --rect.left; --rect.top; --rect.right; --rect.bottom;
1662 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1664 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1667 if (hfontOld)
1668 SelectObject (hdc, hfontOld);
1673 /***********************************************************************
1674 * MENU_DrawPopupMenu
1676 * Paint a popup menu.
1678 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1680 HBRUSH hPrevBrush = 0;
1681 RECT rect;
1683 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1685 GetClientRect( hwnd, &rect );
1687 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1688 && (SelectObject( hdc, get_menu_font(FALSE))))
1690 HPEN hPrevPen;
1692 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1694 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1695 if( hPrevPen )
1697 POPUPMENU *menu;
1698 BOOL flat_menu = FALSE;
1700 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1701 if (flat_menu)
1702 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1703 else
1704 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1706 if( (menu = MENU_GetMenu( hmenu )))
1708 /* draw menu items */
1709 if( menu->nItems)
1711 MENUITEM *item;
1712 UINT u;
1714 item = menu->items;
1715 for( u = menu->nItems; u > 0; u--, item++)
1716 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1717 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1719 /* draw scroll arrows */
1720 if (menu->bScrolling)
1721 MENU_DrawScrollArrows(menu, hdc);
1723 } else
1725 SelectObject( hdc, hPrevBrush );
1730 /***********************************************************************
1731 * MENU_DrawMenuBar
1733 * Paint a menu bar. Returns the height of the menu bar.
1734 * called from [windows/nonclient.c]
1736 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1737 BOOL suppress_draw)
1739 LPPOPUPMENU lppop;
1740 HFONT hfontOld = 0;
1741 HMENU hMenu = GetMenu(hwnd);
1743 lppop = MENU_GetMenu( hMenu );
1744 if (lppop == NULL || lprect == NULL)
1746 return GetSystemMetrics(SM_CYMENU);
1749 if (suppress_draw)
1751 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1753 if (lppop->Height == 0)
1754 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1756 lprect->bottom = lprect->top + lppop->Height;
1758 if (hfontOld) SelectObject( hDC, hfontOld);
1759 return lppop->Height;
1761 else
1762 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1766 /***********************************************************************
1767 * MENU_ShowPopup
1769 * Display a popup menu.
1771 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1772 INT x, INT y, INT xanchor, INT yanchor )
1774 POPUPMENU *menu;
1775 INT width, height;
1776 POINT pt;
1777 HMONITOR monitor;
1778 MONITORINFO info;
1780 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1781 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1783 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1784 if (menu->FocusedItem != NO_SELECTED_ITEM)
1786 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1787 menu->FocusedItem = NO_SELECTED_ITEM;
1790 /* store the owner for DrawItem */
1791 menu->hwndOwner = hwndOwner;
1793 menu->nScrollPos = 0;
1794 MENU_PopupMenuCalcSize( menu );
1796 /* adjust popup menu pos so that it fits within the desktop */
1798 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1799 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1801 /* FIXME: should use item rect */
1802 pt.x = x;
1803 pt.y = y;
1804 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1805 info.cbSize = sizeof(info);
1806 GetMonitorInfoW( monitor, &info );
1807 if( x + width > info.rcWork.right)
1809 if( xanchor && x >= width - xanchor )
1810 x -= width - xanchor;
1812 if( x + width > info.rcWork.right)
1813 x = info.rcWork.right - width;
1815 if( x < info.rcWork.left ) x = info.rcWork.left;
1817 if( y + height > info.rcWork.bottom)
1819 if( yanchor && y >= height + yanchor )
1820 y -= height + yanchor;
1822 if( y + height > info.rcWork.bottom)
1823 y = info.rcWork.bottom - height;
1825 if( y < info.rcWork.top ) y = info.rcWork.top;
1827 /* NOTE: In Windows, top menu popup is not owned. */
1828 menu->hWnd = CreateWindowExW( WS_EX_TOPMOST, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1829 WS_POPUP, x, y, width, height,
1830 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1831 (LPVOID)hmenu );
1832 if( !menu->hWnd ) return FALSE;
1833 if (!top_popup) top_popup = menu->hWnd;
1835 /* Display the window */
1837 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1838 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1839 UpdateWindow( menu->hWnd );
1840 return TRUE;
1844 /***********************************************************************
1845 * MENU_EnsureMenuItemVisible
1847 static void
1848 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1850 if (lppop->bScrolling)
1852 MENUITEM *item = &lppop->items[wIndex];
1853 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1854 UINT nOldPos = lppop->nScrollPos;
1855 RECT rc;
1856 UINT arrow_bitmap_height;
1857 BITMAP bmp;
1859 GetClientRect(lppop->hWnd, &rc);
1861 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1862 arrow_bitmap_height = bmp.bmHeight;
1864 rc.top += arrow_bitmap_height;
1865 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1867 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1868 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1871 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1872 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1873 MENU_DrawScrollArrows(lppop, hdc);
1875 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1877 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1878 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1879 MENU_DrawScrollArrows(lppop, hdc);
1885 /***********************************************************************
1886 * MENU_SelectItem
1888 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1889 BOOL sendMenuSelect, HMENU topmenu )
1891 LPPOPUPMENU lppop;
1892 HDC hdc;
1894 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1896 lppop = MENU_GetMenu( hmenu );
1897 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1899 if (lppop->FocusedItem == wIndex) return;
1900 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1901 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1902 if (!top_popup) top_popup = lppop->hWnd;
1904 SelectObject( hdc, get_menu_font(FALSE));
1906 /* Clear previous highlighted item */
1907 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1909 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1910 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1911 lppop->Height, !(lppop->wFlags & MF_POPUP),
1912 ODA_SELECT );
1915 /* Highlight new item (if any) */
1916 lppop->FocusedItem = wIndex;
1917 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1919 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1920 lppop->items[wIndex].fState |= MF_HILITE;
1921 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1922 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1923 &lppop->items[wIndex], lppop->Height,
1924 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1926 if (sendMenuSelect)
1928 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1929 SendMessageW( hwndOwner, WM_MENUSELECT,
1930 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1931 ip->fType | ip->fState |
1932 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1935 else if (sendMenuSelect) {
1936 if(topmenu){
1937 int pos;
1938 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1939 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1940 MENUITEM *ip = &ptm->items[pos];
1941 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1942 ip->fType | ip->fState |
1943 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1947 ReleaseDC( lppop->hWnd, hdc );
1951 /***********************************************************************
1952 * MENU_MoveSelection
1954 * Moves currently selected item according to the offset parameter.
1955 * If there is no selection then it should select the last item if
1956 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1958 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1960 INT i;
1961 POPUPMENU *menu;
1963 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1965 menu = MENU_GetMenu( hmenu );
1966 if ((!menu) || (!menu->items)) return;
1968 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1970 if( menu->nItems == 1 ) return; else
1971 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1972 ; i += offset)
1973 if (!(menu->items[i].fType & MF_SEPARATOR))
1975 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1976 return;
1980 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1981 i >= 0 && i < menu->nItems ; i += offset)
1982 if (!(menu->items[i].fType & MF_SEPARATOR))
1984 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1985 return;
1990 /**********************************************************************
1991 * MENU_SetItemData
1993 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1994 * ModifyMenu().
1996 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1997 LPCWSTR str )
1999 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2000 TRACE("flags=%x str=%p\n", flags, str);
2002 if (IS_STRING_ITEM(flags))
2004 LPWSTR prevText = item->text;
2005 if (!str)
2007 flags |= MF_SEPARATOR;
2008 item->text = NULL;
2010 else
2012 LPWSTR text;
2013 /* Item beginning with a backspace is a help item */
2014 if (*str == '\b')
2016 flags |= MF_HELP;
2017 str++;
2019 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2020 return FALSE;
2021 strcpyW( text, str );
2022 item->text = text;
2024 item->hbmpItem = NULL;
2025 HeapFree( GetProcessHeap(), 0, prevText );
2027 else if(( flags & MFT_BITMAP)) {
2028 item->hbmpItem = HBITMAP_32(LOWORD(str));
2029 /* setting bitmap clears text */
2030 HeapFree( GetProcessHeap(), 0, item->text );
2031 item->text = NULL;
2034 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2036 if (flags & MF_OWNERDRAW)
2037 item->dwItemData = (DWORD_PTR)str;
2038 else
2039 item->dwItemData = 0;
2041 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2042 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2044 if (flags & MF_POPUP)
2046 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2047 if (menu) menu->wFlags |= MF_POPUP;
2048 else
2050 item->wID = 0;
2051 item->hSubMenu = 0;
2052 item->fType = 0;
2053 item->fState = 0;
2054 return FALSE;
2058 item->wID = id;
2059 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2061 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2062 flags |= MF_POPUP; /* keep popup */
2064 item->fType = flags & TYPE_MASK;
2065 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2066 for ModifyMenu, but Windows accepts it */
2067 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2069 /* Don't call SetRectEmpty here! */
2071 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2072 return TRUE;
2076 /**********************************************************************
2077 * MENU_InsertItem
2079 * Insert (allocate) a new item into a menu.
2081 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2083 MENUITEM *newItems;
2084 POPUPMENU *menu;
2086 if (!(menu = MENU_GetMenu(hMenu)))
2087 return NULL;
2089 /* Find where to insert new item */
2091 if (flags & MF_BYPOSITION) {
2092 if (pos > menu->nItems)
2093 pos = menu->nItems;
2094 } else {
2095 if (!MENU_FindItem( &hMenu, &pos, flags ))
2096 pos = menu->nItems;
2097 else {
2098 if (!(menu = MENU_GetMenu( hMenu )))
2099 return NULL;
2103 /* Make sure that MDI system buttons stay on the right side.
2104 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2105 * regardless of their id.
2107 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2108 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2109 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2110 pos--;
2112 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2114 /* Create new items array */
2116 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2117 if (!newItems)
2119 WARN("allocation failed\n" );
2120 return NULL;
2122 if (menu->nItems > 0)
2124 /* Copy the old array into the new one */
2125 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2126 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2127 (menu->nItems-pos)*sizeof(MENUITEM) );
2128 HeapFree( GetProcessHeap(), 0, menu->items );
2130 menu->items = newItems;
2131 menu->nItems++;
2132 memset( &newItems[pos], 0, sizeof(*newItems) );
2133 menu->Height = 0; /* force size recalculate */
2134 return &newItems[pos];
2138 /**********************************************************************
2139 * MENU_ParseResource
2141 * Parse a standard menu resource and add items to the menu.
2142 * Return a pointer to the end of the resource.
2144 * NOTE: flags is equivalent to the mtOption field
2146 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2148 WORD flags, id = 0;
2149 LPCSTR str;
2150 BOOL end_flag;
2154 flags = GET_WORD(res);
2155 end_flag = flags & MF_END;
2156 /* Remove MF_END because it has the same value as MF_HILITE */
2157 flags &= ~MF_END;
2158 res += sizeof(WORD);
2159 if (!(flags & MF_POPUP))
2161 id = GET_WORD(res);
2162 res += sizeof(WORD);
2164 str = res;
2165 if (!unicode) res += strlen(str) + 1;
2166 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2167 if (flags & MF_POPUP)
2169 HMENU hSubMenu = CreatePopupMenu();
2170 if (!hSubMenu) return NULL;
2171 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2172 return NULL;
2173 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2174 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2176 else /* Not a popup */
2178 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2179 else AppendMenuW( hMenu, flags, id,
2180 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2182 } while (!end_flag);
2183 return res;
2187 /**********************************************************************
2188 * MENUEX_ParseResource
2190 * Parse an extended menu resource and add items to the menu.
2191 * Return a pointer to the end of the resource.
2193 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2195 WORD resinfo;
2196 do {
2197 MENUITEMINFOW mii;
2199 mii.cbSize = sizeof(mii);
2200 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2201 mii.fType = GET_DWORD(res);
2202 res += sizeof(DWORD);
2203 mii.fState = GET_DWORD(res);
2204 res += sizeof(DWORD);
2205 mii.wID = GET_DWORD(res);
2206 res += sizeof(DWORD);
2207 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2208 res += sizeof(WORD);
2209 /* Align the text on a word boundary. */
2210 res += (~((UINT_PTR)res - 1)) & 1;
2211 mii.dwTypeData = (LPWSTR) res;
2212 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2213 /* Align the following fields on a dword boundary. */
2214 res += (~((UINT_PTR)res - 1)) & 3;
2216 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2217 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2219 if (resinfo & 1) { /* Pop-up? */
2220 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2221 res += sizeof(DWORD);
2222 mii.hSubMenu = CreatePopupMenu();
2223 if (!mii.hSubMenu)
2224 return NULL;
2225 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2226 DestroyMenu(mii.hSubMenu);
2227 return NULL;
2229 mii.fMask |= MIIM_SUBMENU;
2230 mii.fType |= MF_POPUP;
2232 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2234 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2235 mii.wID, mii.fType);
2236 mii.fType |= MF_SEPARATOR;
2238 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2239 } while (!(resinfo & MF_END));
2240 return res;
2244 /***********************************************************************
2245 * MENU_GetSubPopup
2247 * Return the handle of the selected sub-popup menu (if any).
2249 static HMENU MENU_GetSubPopup( HMENU hmenu )
2251 POPUPMENU *menu;
2252 MENUITEM *item;
2254 menu = MENU_GetMenu( hmenu );
2256 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2258 item = &menu->items[menu->FocusedItem];
2259 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2260 return item->hSubMenu;
2261 return 0;
2265 /***********************************************************************
2266 * MENU_HideSubPopups
2268 * Hide the sub-popup menus of this menu.
2270 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2271 BOOL sendMenuSelect )
2273 POPUPMENU *menu = MENU_GetMenu( hmenu );
2275 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2277 if (menu && top_popup)
2279 HMENU hsubmenu;
2280 POPUPMENU *submenu;
2281 MENUITEM *item;
2283 if (menu->FocusedItem != NO_SELECTED_ITEM)
2285 item = &menu->items[menu->FocusedItem];
2286 if (!(item->fType & MF_POPUP) ||
2287 !(item->fState & MF_MOUSESELECT)) return;
2288 item->fState &= ~MF_MOUSESELECT;
2289 hsubmenu = item->hSubMenu;
2290 } else return;
2292 submenu = MENU_GetMenu( hsubmenu );
2293 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2294 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2295 DestroyWindow( submenu->hWnd );
2296 submenu->hWnd = 0;
2301 /***********************************************************************
2302 * MENU_ShowSubPopup
2304 * Display the sub-menu of the selected item of this menu.
2305 * Return the handle of the submenu, or hmenu if no submenu to display.
2307 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2308 BOOL selectFirst, UINT wFlags )
2310 RECT rect;
2311 POPUPMENU *menu;
2312 MENUITEM *item;
2313 HDC hdc;
2315 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2317 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2319 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2321 item = &menu->items[menu->FocusedItem];
2322 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2323 return hmenu;
2325 /* message must be sent before using item,
2326 because nearly everything may be changed by the application ! */
2328 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2329 if (!(wFlags & TPM_NONOTIFY))
2330 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2331 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2333 item = &menu->items[menu->FocusedItem];
2334 rect = item->rect;
2336 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2337 if (!(item->fState & MF_HILITE))
2339 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2340 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2342 SelectObject( hdc, get_menu_font(FALSE));
2344 item->fState |= MF_HILITE;
2345 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2346 ReleaseDC( menu->hWnd, hdc );
2348 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2349 item->rect = rect;
2351 item->fState |= MF_MOUSESELECT;
2353 if (IS_SYSTEM_MENU(menu))
2355 MENU_InitSysMenuPopup(item->hSubMenu,
2356 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2357 GetClassLongW( menu->hWnd, GCL_STYLE));
2359 NC_GetSysPopupPos( menu->hWnd, &rect );
2360 rect.top = rect.bottom;
2361 rect.right = GetSystemMetrics(SM_CXSIZE);
2362 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2364 else
2366 GetWindowRect( menu->hWnd, &rect );
2367 if (menu->wFlags & MF_POPUP)
2369 RECT rc = item->rect;
2371 MENU_AdjustMenuItemRect(menu, &rc);
2373 /* The first item in the popup menu has to be at the
2374 same y position as the focused menu item */
2375 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2376 rect.top += rc.top - MENU_TOP_MARGIN;
2377 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2378 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2379 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2381 else
2383 rect.left += item->rect.left;
2384 rect.top += item->rect.bottom;
2385 rect.right = item->rect.right - item->rect.left;
2386 rect.bottom = item->rect.bottom - item->rect.top;
2390 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2391 rect.left, rect.top, rect.right, rect.bottom );
2392 if (selectFirst)
2393 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2394 return item->hSubMenu;
2399 /**********************************************************************
2400 * MENU_IsMenuActive
2402 HWND MENU_IsMenuActive(void)
2404 return top_popup;
2407 /***********************************************************************
2408 * MENU_PtMenu
2410 * Walks menu chain trying to find a menu pt maps to.
2412 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2414 POPUPMENU *menu = MENU_GetMenu( hMenu );
2415 UINT item = menu->FocusedItem;
2416 HMENU ret;
2418 /* try subpopup first (if any) */
2419 ret = (item != NO_SELECTED_ITEM &&
2420 (menu->items[item].fType & MF_POPUP) &&
2421 (menu->items[item].fState & MF_MOUSESELECT))
2422 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2424 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2426 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2427 if( menu->wFlags & MF_POPUP )
2429 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2431 else if (ht == HTSYSMENU)
2432 ret = get_win_sys_menu( menu->hWnd );
2433 else if (ht == HTMENU)
2434 ret = GetMenu( menu->hWnd );
2436 return ret;
2439 /***********************************************************************
2440 * MENU_ExecFocusedItem
2442 * Execute a menu item (for instance when user pressed Enter).
2443 * Return the wID of the executed item. Otherwise, -1 indicating
2444 * that no menu item was executed, -2 if a popup is shown;
2445 * Have to receive the flags for the TrackPopupMenu options to avoid
2446 * sending unwanted message.
2449 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2451 MENUITEM *item;
2452 POPUPMENU *menu = MENU_GetMenu( hMenu );
2454 TRACE("%p hmenu=%p\n", pmt, hMenu);
2456 if (!menu || !menu->nItems ||
2457 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2459 item = &menu->items[menu->FocusedItem];
2461 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2463 if (!(item->fType & MF_POPUP))
2465 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2467 /* If TPM_RETURNCMD is set you return the id, but
2468 do not send a message to the owner */
2469 if(!(wFlags & TPM_RETURNCMD))
2471 if( menu->wFlags & MF_SYSMENU )
2472 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2473 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2474 else
2476 if (menu->dwStyle & MNS_NOTIFYBYPOS)
2477 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2478 (LPARAM)hMenu);
2479 else
2480 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2483 return item->wID;
2486 else
2488 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2489 return -2;
2492 return -1;
2495 /***********************************************************************
2496 * MENU_SwitchTracking
2498 * Helper function for menu navigation routines.
2500 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2502 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2503 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2505 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2507 if( pmt->hTopMenu != hPtMenu &&
2508 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2510 /* both are top level menus (system and menu-bar) */
2511 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2512 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2513 pmt->hTopMenu = hPtMenu;
2515 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2516 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2520 /***********************************************************************
2521 * MENU_ButtonDown
2523 * Return TRUE if we can go on with menu tracking.
2525 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2527 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2529 if (hPtMenu)
2531 UINT id = 0;
2532 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2533 MENUITEM *item;
2535 if( IS_SYSTEM_MENU(ptmenu) )
2536 item = ptmenu->items;
2537 else
2538 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2540 if( item )
2542 if( ptmenu->FocusedItem != id )
2543 MENU_SwitchTracking( pmt, hPtMenu, id );
2545 /* If the popup menu is not already "popped" */
2546 if(!(item->fState & MF_MOUSESELECT ))
2548 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2551 return TRUE;
2553 /* Else the click was on the menu bar, finish the tracking */
2555 return FALSE;
2558 /***********************************************************************
2559 * MENU_ButtonUp
2561 * Return the value of MENU_ExecFocusedItem if
2562 * the selected item was not a popup. Else open the popup.
2563 * A -1 return value indicates that we go on with menu tracking.
2566 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2568 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2570 if (hPtMenu)
2572 UINT id = 0;
2573 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2574 MENUITEM *item;
2576 if( IS_SYSTEM_MENU(ptmenu) )
2577 item = ptmenu->items;
2578 else
2579 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2581 if( item && (ptmenu->FocusedItem == id ))
2583 debug_print_menuitem ("FocusedItem: ", item, "");
2585 if( !(item->fType & MF_POPUP) )
2587 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2588 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2589 return executedMenuId;
2592 /* If we are dealing with the top-level menu */
2593 /* and this is a click on an already "popped" item: */
2594 /* Stop the menu tracking and close the opened submenus */
2595 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2596 return 0;
2598 ptmenu->bTimeToHide = TRUE;
2600 return -1;
2604 /***********************************************************************
2605 * MENU_MouseMove
2607 * Return TRUE if we can go on with menu tracking.
2609 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2611 UINT id = NO_SELECTED_ITEM;
2612 POPUPMENU *ptmenu = NULL;
2614 if( hPtMenu )
2616 ptmenu = MENU_GetMenu( hPtMenu );
2617 if( IS_SYSTEM_MENU(ptmenu) )
2618 id = 0;
2619 else
2620 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2623 if( id == NO_SELECTED_ITEM )
2625 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2626 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2629 else if( ptmenu->FocusedItem != id )
2631 MENU_SwitchTracking( pmt, hPtMenu, id );
2632 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2634 return TRUE;
2638 /***********************************************************************
2639 * MENU_SetCapture
2641 static void MENU_SetCapture( HWND hwnd )
2643 HWND previous = 0;
2645 SERVER_START_REQ( set_capture_window )
2647 req->handle = hwnd;
2648 req->flags = CAPTURE_MENU;
2649 if (!wine_server_call_err( req ))
2651 previous = reply->previous;
2652 hwnd = reply->full_handle;
2655 SERVER_END_REQ;
2657 if (previous && previous != hwnd)
2658 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2662 /***********************************************************************
2663 * MENU_DoNextMenu
2665 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2667 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2669 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2670 BOOL atEnd = FALSE;
2672 /* When skipping left, we need to do something special after the
2673 first menu. */
2674 if (vk == VK_LEFT && menu->FocusedItem == 0)
2676 atEnd = TRUE;
2678 /* When skipping right, for the non-system menu, we need to
2679 handle the last non-special menu item (ie skip any window
2680 icons such as MDI maximize, restore or close) */
2681 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2683 int i = menu->FocusedItem + 1;
2684 while (i < menu->nItems) {
2685 if ((menu->items[i].wID >= SC_SIZE &&
2686 menu->items[i].wID <= SC_RESTORE)) {
2687 i++;
2688 } else break;
2690 if (i == menu->nItems) {
2691 atEnd = TRUE;
2694 /* When skipping right, we need to cater for the system menu */
2695 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2697 if (menu->FocusedItem == (menu->nItems - 1)) {
2698 atEnd = TRUE;
2702 if( atEnd )
2704 MDINEXTMENU next_menu;
2705 HMENU hNewMenu;
2706 HWND hNewWnd;
2707 UINT id = 0;
2709 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2710 next_menu.hmenuNext = 0;
2711 next_menu.hwndNext = 0;
2712 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2714 TRACE("%p [%p] -> %p [%p]\n",
2715 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2717 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2719 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2720 hNewWnd = pmt->hOwnerWnd;
2721 if( IS_SYSTEM_MENU(menu) )
2723 /* switch to the menu bar */
2725 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2727 if( vk == VK_LEFT )
2729 menu = MENU_GetMenu( hNewMenu );
2730 id = menu->nItems - 1;
2732 /* Skip backwards over any system predefined icons,
2733 eg. MDI close, restore etc icons */
2734 while ((id > 0) &&
2735 (menu->items[id].wID >= SC_SIZE &&
2736 menu->items[id].wID <= SC_RESTORE)) id--;
2739 else if (style & WS_SYSMENU )
2741 /* switch to the system menu */
2742 hNewMenu = get_win_sys_menu( hNewWnd );
2744 else return FALSE;
2746 else /* application returned a new menu to switch to */
2748 hNewMenu = next_menu.hmenuNext;
2749 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2751 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2753 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2755 if (style & WS_SYSMENU &&
2756 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2758 /* get the real system menu */
2759 hNewMenu = get_win_sys_menu(hNewWnd);
2761 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2763 /* FIXME: Not sure what to do here;
2764 * perhaps try to track hNewMenu as a popup? */
2766 TRACE(" -- got confused.\n");
2767 return FALSE;
2770 else return FALSE;
2773 if( hNewMenu != pmt->hTopMenu )
2775 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2776 FALSE, 0 );
2777 if( pmt->hCurrentMenu != pmt->hTopMenu )
2778 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2781 if( hNewWnd != pmt->hOwnerWnd )
2783 pmt->hOwnerWnd = hNewWnd;
2784 MENU_SetCapture( pmt->hOwnerWnd );
2787 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2788 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2790 return TRUE;
2792 return FALSE;
2795 /***********************************************************************
2796 * MENU_SuspendPopup
2798 * The idea is not to show the popup if the next input message is
2799 * going to hide it anyway.
2801 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2803 MSG msg;
2805 msg.hwnd = pmt->hOwnerWnd;
2807 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2808 pmt->trackFlags |= TF_SKIPREMOVE;
2810 switch( uMsg )
2812 case WM_KEYDOWN:
2813 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2814 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2816 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2817 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2818 if( msg.message == WM_KEYDOWN &&
2819 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2821 pmt->trackFlags |= TF_SUSPENDPOPUP;
2822 return TRUE;
2825 break;
2828 /* failures go through this */
2829 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2830 return FALSE;
2833 /***********************************************************************
2834 * MENU_KeyEscape
2836 * Handle a VK_ESCAPE key event in a menu.
2838 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2840 BOOL bEndMenu = TRUE;
2842 if (pmt->hCurrentMenu != pmt->hTopMenu)
2844 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2846 if (menu->wFlags & MF_POPUP)
2848 HMENU hmenutmp, hmenuprev;
2850 hmenuprev = hmenutmp = pmt->hTopMenu;
2852 /* close topmost popup */
2853 while (hmenutmp != pmt->hCurrentMenu)
2855 hmenuprev = hmenutmp;
2856 hmenutmp = MENU_GetSubPopup( hmenuprev );
2859 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2860 pmt->hCurrentMenu = hmenuprev;
2861 bEndMenu = FALSE;
2865 return bEndMenu;
2868 /***********************************************************************
2869 * MENU_KeyLeft
2871 * Handle a VK_LEFT key event in a menu.
2873 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2875 POPUPMENU *menu;
2876 HMENU hmenutmp, hmenuprev;
2877 UINT prevcol;
2879 hmenuprev = hmenutmp = pmt->hTopMenu;
2880 menu = MENU_GetMenu( hmenutmp );
2882 /* Try to move 1 column left (if possible) */
2883 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2884 NO_SELECTED_ITEM ) {
2886 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2887 prevcol, TRUE, 0 );
2888 return;
2891 /* close topmost popup */
2892 while (hmenutmp != pmt->hCurrentMenu)
2894 hmenuprev = hmenutmp;
2895 hmenutmp = MENU_GetSubPopup( hmenuprev );
2898 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2899 pmt->hCurrentMenu = hmenuprev;
2901 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2903 /* move menu bar selection if no more popups are left */
2905 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2906 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2908 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2910 /* A sublevel menu was displayed - display the next one
2911 * unless there is another displacement coming up */
2913 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2914 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2915 pmt->hTopMenu, TRUE, wFlags);
2921 /***********************************************************************
2922 * MENU_KeyRight
2924 * Handle a VK_RIGHT key event in a menu.
2926 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2928 HMENU hmenutmp;
2929 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2930 UINT nextcol;
2932 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2933 pmt->hCurrentMenu,
2934 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2935 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2937 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2939 /* If already displaying a popup, try to display sub-popup */
2941 hmenutmp = pmt->hCurrentMenu;
2942 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2944 /* if subpopup was displayed then we are done */
2945 if (hmenutmp != pmt->hCurrentMenu) return;
2948 /* Check to see if there's another column */
2949 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2950 NO_SELECTED_ITEM ) {
2951 TRACE("Going to %d.\n", nextcol );
2952 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2953 nextcol, TRUE, 0 );
2954 return;
2957 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2959 if( pmt->hCurrentMenu != pmt->hTopMenu )
2961 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2962 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2963 } else hmenutmp = 0;
2965 /* try to move to the next item */
2966 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2967 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2969 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2970 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2971 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2972 pmt->hTopMenu, TRUE, wFlags);
2976 /***********************************************************************
2977 * MENU_TrackMenu
2979 * Menu tracking code.
2981 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2982 HWND hwnd, const RECT *lprect )
2984 MSG msg;
2985 POPUPMENU *menu;
2986 BOOL fRemove;
2987 INT executedMenuId = -1;
2988 MTRACKER mt;
2989 BOOL enterIdleSent = FALSE;
2991 mt.trackFlags = 0;
2992 mt.hCurrentMenu = hmenu;
2993 mt.hTopMenu = hmenu;
2994 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2995 mt.pt.x = x;
2996 mt.pt.y = y;
2998 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2999 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3001 fEndMenu = FALSE;
3002 if (!(menu = MENU_GetMenu( hmenu )))
3004 WARN("Invalid menu handle %p\n", hmenu);
3005 SetLastError(ERROR_INVALID_MENU_HANDLE);
3006 return FALSE;
3009 if (wFlags & TPM_BUTTONDOWN)
3011 /* Get the result in order to start the tracking or not */
3012 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3013 fEndMenu = !fRemove;
3016 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3018 MENU_SetCapture( mt.hOwnerWnd );
3020 while (!fEndMenu)
3022 menu = MENU_GetMenu( mt.hCurrentMenu );
3023 if (!menu) /* sometimes happens if I do a window manager close */
3024 break;
3026 /* we have to keep the message in the queue until it's
3027 * clear that menu loop is not over yet. */
3029 for (;;)
3031 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3033 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3034 /* remove the message from the queue */
3035 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3037 else
3039 if (!enterIdleSent)
3041 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3042 enterIdleSent = TRUE;
3043 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3045 WaitMessage();
3049 /* check if EndMenu() tried to cancel us, by posting this message */
3050 if(msg.message == WM_CANCELMODE)
3052 /* we are now out of the loop */
3053 fEndMenu = TRUE;
3055 /* remove the message from the queue */
3056 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3058 /* break out of internal loop, ala ESCAPE */
3059 break;
3062 TranslateMessage( &msg );
3063 mt.pt = msg.pt;
3065 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3066 enterIdleSent=FALSE;
3068 fRemove = FALSE;
3069 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3072 * Use the mouse coordinates in lParam instead of those in the MSG
3073 * struct to properly handle synthetic messages. They are already
3074 * in screen coordinates.
3076 mt.pt.x = (short)LOWORD(msg.lParam);
3077 mt.pt.y = (short)HIWORD(msg.lParam);
3079 /* Find a menu for this mouse event */
3080 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3082 switch(msg.message)
3084 /* no WM_NC... messages in captured state */
3086 case WM_RBUTTONDBLCLK:
3087 case WM_RBUTTONDOWN:
3088 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3089 /* fall through */
3090 case WM_LBUTTONDBLCLK:
3091 case WM_LBUTTONDOWN:
3092 /* If the message belongs to the menu, removes it from the queue */
3093 /* Else, end menu tracking */
3094 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3095 fEndMenu = !fRemove;
3096 break;
3098 case WM_RBUTTONUP:
3099 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3100 /* fall through */
3101 case WM_LBUTTONUP:
3102 /* Check if a menu was selected by the mouse */
3103 if (hmenu)
3105 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3106 TRACE("executedMenuId %d\n", executedMenuId);
3108 /* End the loop if executedMenuId is an item ID */
3109 /* or if the job was done (executedMenuId = 0). */
3110 fEndMenu = fRemove = (executedMenuId != -1);
3112 /* No menu was selected by the mouse */
3113 /* if the function was called by TrackPopupMenu, continue
3114 with the menu tracking. If not, stop it */
3115 else
3116 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3118 break;
3120 case WM_MOUSEMOVE:
3121 /* the selected menu item must be changed every time */
3122 /* the mouse moves. */
3124 if (hmenu)
3125 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3127 } /* switch(msg.message) - mouse */
3129 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3131 fRemove = TRUE; /* Keyboard messages are always removed */
3132 switch(msg.message)
3134 case WM_KEYDOWN:
3135 case WM_SYSKEYDOWN:
3136 switch(msg.wParam)
3138 case VK_MENU:
3139 fEndMenu = TRUE;
3140 break;
3142 case VK_HOME:
3143 case VK_END:
3144 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3145 NO_SELECTED_ITEM, FALSE, 0 );
3146 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3147 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3148 break;
3150 case VK_UP:
3151 case VK_DOWN: /* If on menu bar, pull-down the menu */
3153 menu = MENU_GetMenu( mt.hCurrentMenu );
3154 if (!(menu->wFlags & MF_POPUP))
3155 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3156 else /* otherwise try to move selection */
3157 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3158 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3159 break;
3161 case VK_LEFT:
3162 MENU_KeyLeft( &mt, wFlags );
3163 break;
3165 case VK_RIGHT:
3166 MENU_KeyRight( &mt, wFlags );
3167 break;
3169 case VK_ESCAPE:
3170 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3171 break;
3173 case VK_F1:
3175 HELPINFO hi;
3176 hi.cbSize = sizeof(HELPINFO);
3177 hi.iContextType = HELPINFO_MENUITEM;
3178 if (menu->FocusedItem == NO_SELECTED_ITEM)
3179 hi.iCtrlId = 0;
3180 else
3181 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3182 hi.hItemHandle = hmenu;
3183 hi.dwContextId = menu->dwContextHelpID;
3184 hi.MousePos = msg.pt;
3185 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3186 break;
3189 default:
3190 break;
3192 break; /* WM_KEYDOWN */
3194 case WM_CHAR:
3195 case WM_SYSCHAR:
3197 UINT pos;
3199 if (msg.wParam == '\r' || msg.wParam == ' ')
3201 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3202 fEndMenu = (executedMenuId != -2);
3204 break;
3207 /* Hack to avoid control chars. */
3208 /* We will find a better way real soon... */
3209 if (msg.wParam < 32) break;
3211 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3212 LOWORD(msg.wParam), FALSE );
3213 if (pos == (UINT)-2) fEndMenu = TRUE;
3214 else if (pos == (UINT)-1) MessageBeep(0);
3215 else
3217 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3218 TRUE, 0 );
3219 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3220 fEndMenu = (executedMenuId != -2);
3223 break;
3224 } /* switch(msg.message) - kbd */
3226 else
3228 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3229 DispatchMessageW( &msg );
3230 continue;
3233 if (!fEndMenu) fRemove = TRUE;
3235 /* finally remove message from the queue */
3237 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3238 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3239 else mt.trackFlags &= ~TF_SKIPREMOVE;
3242 MENU_SetCapture(0); /* release the capture */
3244 /* If dropdown is still painted and the close box is clicked on
3245 then the menu will be destroyed as part of the DispatchMessage above.
3246 This will then invalidate the menu handle in mt.hTopMenu. We should
3247 check for this first. */
3248 if( IsMenu( mt.hTopMenu ) )
3250 menu = MENU_GetMenu( mt.hTopMenu );
3252 if( IsWindow( mt.hOwnerWnd ) )
3254 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3256 if (menu && (menu->wFlags & MF_POPUP))
3258 DestroyWindow( menu->hWnd );
3259 menu->hWnd = 0;
3261 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3262 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3265 /* Reset the variable for hiding menu */
3266 if( menu ) menu->bTimeToHide = FALSE;
3269 /* The return value is only used by TrackPopupMenu */
3270 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3271 if (executedMenuId == -1) executedMenuId = 0;
3272 return executedMenuId;
3275 /***********************************************************************
3276 * MENU_InitTracking
3278 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3280 POPUPMENU *menu;
3282 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3284 HideCaret(0);
3286 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3287 if (!(wFlags & TPM_NONOTIFY))
3288 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3290 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3292 if (!(wFlags & TPM_NONOTIFY))
3294 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3295 /* If an app changed/recreated menu bar entries in WM_INITMENU
3296 * menu sizes will be recalculated once the menu created/shown.
3300 /* This makes the menus of applications built with Delphi work.
3301 * It also enables menus to be displayed in more than one window,
3302 * but there are some bugs left that need to be fixed in this case.
3304 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3306 return TRUE;
3308 /***********************************************************************
3309 * MENU_ExitTracking
3311 static BOOL MENU_ExitTracking(HWND hWnd)
3313 TRACE("hwnd=%p\n", hWnd);
3315 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3316 ShowCaret(0);
3317 top_popup = 0;
3318 return TRUE;
3321 /***********************************************************************
3322 * MENU_TrackMouseMenuBar
3324 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3326 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3328 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3329 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3331 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3333 if (IsMenu(hMenu))
3335 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3336 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3337 MENU_ExitTracking(hWnd);
3342 /***********************************************************************
3343 * MENU_TrackKbdMenuBar
3345 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3347 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3349 UINT uItem = NO_SELECTED_ITEM;
3350 HMENU hTrackMenu;
3351 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3353 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3355 /* find window that has a menu */
3357 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3358 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3360 /* check if we have to track a system menu */
3362 hTrackMenu = GetMenu( hwnd );
3363 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3365 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3366 hTrackMenu = get_win_sys_menu( hwnd );
3367 uItem = 0;
3368 wParam |= HTSYSMENU; /* prevent item lookup */
3371 if (!IsMenu( hTrackMenu )) return;
3373 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3375 if( wChar && wChar != ' ' )
3377 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3378 if ( uItem >= (UINT)(-2) )
3380 if( uItem == (UINT)(-1) ) MessageBeep(0);
3381 /* schedule end of menu tracking */
3382 wFlags |= TF_ENDMENU;
3383 goto track_menu;
3387 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3389 if (wParam & HTSYSMENU && wChar != ' ')
3391 /* prevent sysmenu activation for managed windows on Alt down/up */
3392 if (GetPropA( hwnd, "__wine_x11_managed" ))
3393 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3395 else
3397 if( uItem == NO_SELECTED_ITEM )
3398 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3399 else
3400 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3403 track_menu:
3404 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3405 MENU_ExitTracking( hwnd );
3409 /**********************************************************************
3410 * TrackPopupMenu (USER32.@)
3412 * Like the win32 API, the function return the command ID only if the
3413 * flag TPM_RETURNCMD is on.
3416 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3417 INT nReserved, HWND hWnd, const RECT *lpRect )
3419 BOOL ret = FALSE;
3421 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3422 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3424 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3426 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3427 if (!(wFlags & TPM_NONOTIFY))
3428 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3430 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3431 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3432 MENU_ExitTracking(hWnd);
3434 return ret;
3437 /**********************************************************************
3438 * TrackPopupMenuEx (USER32.@)
3440 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3441 HWND hWnd, LPTPMPARAMS lpTpm )
3443 FIXME("not fully implemented\n" );
3444 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3445 lpTpm ? &lpTpm->rcExclude : NULL );
3448 /***********************************************************************
3449 * PopupMenuWndProc
3451 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3453 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3455 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3457 switch(message)
3459 case WM_CREATE:
3461 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3462 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3463 return 0;
3466 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3467 return MA_NOACTIVATE;
3469 case WM_PAINT:
3471 PAINTSTRUCT ps;
3472 BeginPaint( hwnd, &ps );
3473 MENU_DrawPopupMenu( hwnd, ps.hdc,
3474 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3475 EndPaint( hwnd, &ps );
3476 return 0;
3478 case WM_ERASEBKGND:
3479 return 1;
3481 case WM_DESTROY:
3482 /* zero out global pointer in case resident popup window was destroyed. */
3483 if (hwnd == top_popup) top_popup = 0;
3484 break;
3486 case WM_SHOWWINDOW:
3488 if( wParam )
3490 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3492 else
3493 SetWindowLongPtrW( hwnd, 0, 0 );
3494 break;
3496 case MM_SETMENUHANDLE:
3497 SetWindowLongPtrW( hwnd, 0, wParam );
3498 break;
3500 case MM_GETMENUHANDLE:
3501 return GetWindowLongPtrW( hwnd, 0 );
3503 default:
3504 return DefWindowProcW( hwnd, message, wParam, lParam );
3506 return 0;
3510 /***********************************************************************
3511 * MENU_GetMenuBarHeight
3513 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3515 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3516 INT orgX, INT orgY )
3518 HDC hdc;
3519 RECT rectBar;
3520 LPPOPUPMENU lppop;
3522 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3524 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3526 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3527 SelectObject( hdc, get_menu_font(FALSE));
3528 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3529 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3530 ReleaseDC( hwnd, hdc );
3531 return lppop->Height;
3535 /*******************************************************************
3536 * ChangeMenuA (USER32.@)
3538 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3539 UINT id, UINT flags )
3541 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3542 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3543 id, data );
3544 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3545 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3546 id, data );
3547 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3548 flags & MF_BYPOSITION ? pos : id,
3549 flags & ~MF_REMOVE );
3550 /* Default: MF_INSERT */
3551 return InsertMenuA( hMenu, pos, flags, id, data );
3555 /*******************************************************************
3556 * ChangeMenuW (USER32.@)
3558 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3559 UINT id, UINT flags )
3561 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3562 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3563 id, data );
3564 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3565 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3566 id, data );
3567 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3568 flags & MF_BYPOSITION ? pos : id,
3569 flags & ~MF_REMOVE );
3570 /* Default: MF_INSERT */
3571 return InsertMenuW( hMenu, pos, flags, id, data );
3575 /*******************************************************************
3576 * CheckMenuItem (USER32.@)
3578 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3580 MENUITEM *item;
3581 DWORD ret;
3583 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3584 ret = item->fState & MF_CHECKED;
3585 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3586 else item->fState &= ~MF_CHECKED;
3587 return ret;
3591 /**********************************************************************
3592 * EnableMenuItem (USER32.@)
3594 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3596 UINT oldflags;
3597 MENUITEM *item;
3598 POPUPMENU *menu;
3600 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3602 /* Get the Popupmenu to access the owner menu */
3603 if (!(menu = MENU_GetMenu(hMenu)))
3604 return (UINT)-1;
3606 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3607 return (UINT)-1;
3609 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3610 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3612 /* If the close item in the system menu change update the close button */
3613 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3615 if (menu->hSysMenuOwner != 0)
3617 RECT rc;
3618 POPUPMENU* parentMenu;
3620 /* Get the parent menu to access*/
3621 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3622 return (UINT)-1;
3624 /* Refresh the frame to reflect the change */
3625 GetWindowRect(parentMenu->hWnd, &rc);
3626 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3627 rc.bottom = 0;
3628 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3632 return oldflags;
3636 /*******************************************************************
3637 * GetMenuStringA (USER32.@)
3639 INT WINAPI GetMenuStringA(
3640 HMENU hMenu, /* [in] menuhandle */
3641 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3642 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3643 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3644 UINT wFlags /* [in] MF_ flags */
3646 MENUITEM *item;
3648 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3649 if (str && nMaxSiz) str[0] = '\0';
3650 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3651 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3652 return 0;
3654 if (!item->text) return 0;
3655 if (!str || !nMaxSiz) return strlenW(item->text);
3656 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3657 str[nMaxSiz-1] = 0;
3658 TRACE("returning %s\n", debugstr_a(str));
3659 return strlen(str);
3663 /*******************************************************************
3664 * GetMenuStringW (USER32.@)
3666 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3667 LPWSTR str, INT nMaxSiz, UINT wFlags )
3669 MENUITEM *item;
3671 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3672 if (str && nMaxSiz) str[0] = '\0';
3673 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3674 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3675 return 0;
3677 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3678 if( !(item->text)) {
3679 str[0] = 0;
3680 return 0;
3682 lstrcpynW( str, item->text, nMaxSiz );
3683 TRACE("returning %s\n", debugstr_w(str));
3684 return strlenW(str);
3688 /**********************************************************************
3689 * HiliteMenuItem (USER32.@)
3691 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3692 UINT wHilite )
3694 LPPOPUPMENU menu;
3695 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3696 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3697 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3698 if (menu->FocusedItem == wItemID) return TRUE;
3699 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3700 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3701 return TRUE;
3705 /**********************************************************************
3706 * GetMenuState (USER32.@)
3708 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3710 MENUITEM *item;
3711 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3712 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3713 debug_print_menuitem (" item: ", item, "");
3714 if (item->fType & MF_POPUP)
3716 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3717 if (!menu) return -1;
3718 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3720 else
3722 /* We used to (from way back then) mask the result to 0xff. */
3723 /* I don't know why and it seems wrong as the documented */
3724 /* return flag MF_SEPARATOR is outside that mask. */
3725 return (item->fType | item->fState);
3730 /**********************************************************************
3731 * GetMenuItemCount (USER32.@)
3733 INT WINAPI GetMenuItemCount( HMENU hMenu )
3735 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3736 if (!menu) return -1;
3737 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3738 return menu->nItems;
3742 /**********************************************************************
3743 * GetMenuItemID (USER32.@)
3745 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3747 MENUITEM * lpmi;
3749 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3750 if (lpmi->fType & MF_POPUP) return -1;
3751 return lpmi->wID;
3756 /*******************************************************************
3757 * InsertMenuW (USER32.@)
3759 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3760 UINT_PTR id, LPCWSTR str )
3762 MENUITEM *item;
3764 if (IS_STRING_ITEM(flags) && str)
3765 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3766 hMenu, pos, flags, id, debugstr_w(str) );
3767 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3768 hMenu, pos, flags, id, str );
3770 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3772 if (!(MENU_SetItemData( item, flags, id, str )))
3774 RemoveMenu( hMenu, pos, flags );
3775 return FALSE;
3778 item->hCheckBit = item->hUnCheckBit = 0;
3779 return TRUE;
3783 /*******************************************************************
3784 * InsertMenuA (USER32.@)
3786 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3787 UINT_PTR id, LPCSTR str )
3789 BOOL ret = FALSE;
3791 if (IS_STRING_ITEM(flags) && str)
3793 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3794 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3795 if (newstr)
3797 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3798 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3799 HeapFree( GetProcessHeap(), 0, newstr );
3801 return ret;
3803 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3807 /*******************************************************************
3808 * AppendMenuA (USER32.@)
3810 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3811 UINT_PTR id, LPCSTR data )
3813 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3817 /*******************************************************************
3818 * AppendMenuW (USER32.@)
3820 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3821 UINT_PTR id, LPCWSTR data )
3823 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3827 /**********************************************************************
3828 * RemoveMenu (USER32.@)
3830 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3832 LPPOPUPMENU menu;
3833 MENUITEM *item;
3835 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3836 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3837 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3839 /* Remove item */
3841 MENU_FreeItemData( item );
3843 if (--menu->nItems == 0)
3845 HeapFree( GetProcessHeap(), 0, menu->items );
3846 menu->items = NULL;
3848 else
3850 while(nPos < menu->nItems)
3852 *item = *(item+1);
3853 item++;
3854 nPos++;
3856 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3857 menu->nItems * sizeof(MENUITEM) );
3859 return TRUE;
3863 /**********************************************************************
3864 * DeleteMenu (USER32.@)
3866 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3868 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3869 if (!item) return FALSE;
3870 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3871 /* nPos is now the position of the item */
3872 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3873 return TRUE;
3877 /*******************************************************************
3878 * ModifyMenuW (USER32.@)
3880 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3881 UINT_PTR id, LPCWSTR str )
3883 MENUITEM *item;
3885 if (IS_STRING_ITEM(flags))
3886 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3887 else
3888 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3890 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3891 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3892 return MENU_SetItemData( item, flags, id, str );
3896 /*******************************************************************
3897 * ModifyMenuA (USER32.@)
3899 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3900 UINT_PTR id, LPCSTR str )
3902 BOOL ret = FALSE;
3904 if (IS_STRING_ITEM(flags) && str)
3906 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3907 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3908 if (newstr)
3910 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3911 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3912 HeapFree( GetProcessHeap(), 0, newstr );
3914 return ret;
3916 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3920 /**********************************************************************
3921 * CreatePopupMenu (USER32.@)
3923 HMENU WINAPI CreatePopupMenu(void)
3925 HMENU hmenu;
3926 POPUPMENU *menu;
3928 if (!(hmenu = CreateMenu())) return 0;
3929 menu = MENU_GetMenu( hmenu );
3930 menu->wFlags |= MF_POPUP;
3931 menu->bTimeToHide = FALSE;
3932 return hmenu;
3936 /**********************************************************************
3937 * GetMenuCheckMarkDimensions (USER.417)
3938 * GetMenuCheckMarkDimensions (USER32.@)
3940 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3942 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3946 /**********************************************************************
3947 * SetMenuItemBitmaps (USER32.@)
3949 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3950 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3952 MENUITEM *item;
3954 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3956 if (!hNewCheck && !hNewUnCheck)
3958 item->fState &= ~MF_USECHECKBITMAPS;
3960 else /* Install new bitmaps */
3962 item->hCheckBit = hNewCheck;
3963 item->hUnCheckBit = hNewUnCheck;
3964 item->fState |= MF_USECHECKBITMAPS;
3966 return TRUE;
3970 /**********************************************************************
3971 * CreateMenu (USER32.@)
3973 HMENU WINAPI CreateMenu(void)
3975 HMENU hMenu;
3976 LPPOPUPMENU menu;
3977 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3978 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3980 ZeroMemory(menu, sizeof(POPUPMENU));
3981 menu->wMagic = MENU_MAGIC;
3982 menu->FocusedItem = NO_SELECTED_ITEM;
3983 menu->bTimeToHide = FALSE;
3985 TRACE("return %p\n", hMenu );
3987 return hMenu;
3991 /**********************************************************************
3992 * DestroyMenu (USER32.@)
3994 BOOL WINAPI DestroyMenu( HMENU hMenu )
3996 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3998 TRACE("(%p)\n", hMenu);
4001 if (!lppop) return FALSE;
4003 lppop->wMagic = 0; /* Mark it as destroyed */
4005 /* DestroyMenu should not destroy system menu popup owner */
4006 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4008 DestroyWindow( lppop->hWnd );
4009 lppop->hWnd = 0;
4012 if (lppop->items) /* recursively destroy submenus */
4014 int i;
4015 MENUITEM *item = lppop->items;
4016 for (i = lppop->nItems; i > 0; i--, item++)
4018 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4019 MENU_FreeItemData( item );
4021 HeapFree( GetProcessHeap(), 0, lppop->items );
4023 USER_HEAP_FREE( hMenu );
4024 return TRUE;
4028 /**********************************************************************
4029 * GetSystemMenu (USER32.@)
4031 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4033 WND *wndPtr = WIN_GetPtr( hWnd );
4034 HMENU retvalue = 0;
4036 if (wndPtr == WND_DESKTOP) return 0;
4037 if (wndPtr == WND_OTHER_PROCESS)
4039 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4041 else if (wndPtr)
4043 if (wndPtr->hSysMenu && bRevert)
4045 DestroyMenu(wndPtr->hSysMenu);
4046 wndPtr->hSysMenu = 0;
4049 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4050 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4052 if( wndPtr->hSysMenu )
4054 POPUPMENU *menu;
4055 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4057 /* Store the dummy sysmenu handle to facilitate the refresh */
4058 /* of the close button if the SC_CLOSE item change */
4059 menu = MENU_GetMenu(retvalue);
4060 if ( menu )
4061 menu->hSysMenuOwner = wndPtr->hSysMenu;
4063 WIN_ReleasePtr( wndPtr );
4065 return bRevert ? 0 : retvalue;
4069 /*******************************************************************
4070 * SetSystemMenu (USER32.@)
4072 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4074 WND *wndPtr = WIN_GetPtr( hwnd );
4076 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4078 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4079 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4080 WIN_ReleasePtr( wndPtr );
4081 return TRUE;
4083 return FALSE;
4087 /**********************************************************************
4088 * GetMenu (USER32.@)
4090 HMENU WINAPI GetMenu( HWND hWnd )
4092 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4093 TRACE("for %p returning %p\n", hWnd, retvalue);
4094 return retvalue;
4097 /**********************************************************************
4098 * GetMenuBarInfo (USER32.@)
4100 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4102 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4103 return FALSE;
4106 /**********************************************************************
4107 * MENU_SetMenu
4109 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4110 * SetWindowPos call that would result if SetMenu were called directly.
4112 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4114 TRACE("(%p, %p);\n", hWnd, hMenu);
4116 if (hMenu && !IsMenu(hMenu))
4118 WARN("hMenu %p is not a menu handle\n", hMenu);
4119 return FALSE;
4121 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4122 return FALSE;
4124 hWnd = WIN_GetFullHandle( hWnd );
4125 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4127 if (hMenu != 0)
4129 LPPOPUPMENU lpmenu;
4131 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4133 lpmenu->hWnd = hWnd;
4134 lpmenu->Height = 0; /* Make sure we recalculate the size */
4136 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4137 return TRUE;
4141 /**********************************************************************
4142 * SetMenu (USER32.@)
4144 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4146 if(!MENU_SetMenu(hWnd, hMenu))
4147 return FALSE;
4149 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4150 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4151 return TRUE;
4155 /**********************************************************************
4156 * GetSubMenu (USER32.@)
4158 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4160 MENUITEM * lpmi;
4162 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4163 if (!(lpmi->fType & MF_POPUP)) return 0;
4164 return lpmi->hSubMenu;
4168 /**********************************************************************
4169 * DrawMenuBar (USER32.@)
4171 BOOL WINAPI DrawMenuBar( HWND hWnd )
4173 LPPOPUPMENU lppop;
4174 HMENU hMenu = GetMenu(hWnd);
4176 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4177 return FALSE;
4178 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4180 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4181 lppop->hwndOwner = hWnd;
4182 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4183 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4184 return TRUE;
4187 /***********************************************************************
4188 * DrawMenuBarTemp (USER32.@)
4190 * UNDOCUMENTED !!
4192 * called by W98SE desk.cpl Control Panel Applet
4194 * Not 100% sure about the param names, but close.
4196 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4198 LPPOPUPMENU lppop;
4199 UINT i,retvalue;
4200 HFONT hfontOld = 0;
4201 BOOL flat_menu = FALSE;
4203 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4205 if (!hMenu)
4206 hMenu = GetMenu(hwnd);
4208 if (!hFont)
4209 hFont = get_menu_font(FALSE);
4211 lppop = MENU_GetMenu( hMenu );
4212 if (lppop == NULL || lprect == NULL)
4214 retvalue = GetSystemMetrics(SM_CYMENU);
4215 goto END;
4218 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4220 hfontOld = SelectObject( hDC, hFont);
4222 if (lppop->Height == 0)
4223 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4225 lprect->bottom = lprect->top + lppop->Height;
4227 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4229 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4230 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4231 LineTo( hDC, lprect->right, lprect->bottom );
4233 if (lppop->nItems == 0)
4235 retvalue = GetSystemMetrics(SM_CYMENU);
4236 goto END;
4239 for (i = 0; i < lppop->nItems; i++)
4241 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4242 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4244 retvalue = lppop->Height;
4246 END:
4247 if (hfontOld) SelectObject (hDC, hfontOld);
4248 return retvalue;
4251 /***********************************************************************
4252 * EndMenu (USER.187)
4253 * EndMenu (USER32.@)
4255 void WINAPI EndMenu(void)
4257 /* if we are in the menu code, and it is active */
4258 if (!fEndMenu && top_popup)
4260 /* terminate the menu handling code */
4261 fEndMenu = TRUE;
4263 /* needs to be posted to wakeup the internal menu handler */
4264 /* which will now terminate the menu, in the event that */
4265 /* the main window was minimized, or lost focus, so we */
4266 /* don't end up with an orphaned menu */
4267 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4272 /***********************************************************************
4273 * LookupMenuHandle (USER.217)
4275 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4277 HMENU hmenu32 = HMENU_32(hmenu);
4278 UINT id32 = id;
4279 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4280 else return HMENU_16(hmenu32);
4284 /**********************************************************************
4285 * LoadMenu (USER.150)
4287 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4289 HRSRC16 hRsrc;
4290 HGLOBAL16 handle;
4291 HMENU16 hMenu;
4293 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4294 if (!name) return 0;
4296 instance = GetExePtr( instance );
4297 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4298 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4299 hMenu = LoadMenuIndirect16(LockResource16(handle));
4300 FreeResource16( handle );
4301 return hMenu;
4305 /*****************************************************************
4306 * LoadMenuA (USER32.@)
4308 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4310 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4311 if (!hrsrc) return 0;
4312 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4316 /*****************************************************************
4317 * LoadMenuW (USER32.@)
4319 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4321 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4322 if (!hrsrc) return 0;
4323 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4327 /**********************************************************************
4328 * LoadMenuIndirect (USER.220)
4330 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4332 HMENU hMenu;
4333 WORD version, offset;
4334 LPCSTR p = (LPCSTR)template;
4336 TRACE("(%p)\n", template );
4337 version = GET_WORD(p);
4338 p += sizeof(WORD);
4339 if (version)
4341 WARN("version must be 0 for Win16\n" );
4342 return 0;
4344 offset = GET_WORD(p);
4345 p += sizeof(WORD) + offset;
4346 if (!(hMenu = CreateMenu())) return 0;
4347 if (!MENU_ParseResource( p, hMenu, FALSE ))
4349 DestroyMenu( hMenu );
4350 return 0;
4352 return HMENU_16(hMenu);
4356 /**********************************************************************
4357 * LoadMenuIndirectW (USER32.@)
4359 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4361 HMENU hMenu;
4362 WORD version, offset;
4363 LPCSTR p = (LPCSTR)template;
4365 version = GET_WORD(p);
4366 p += sizeof(WORD);
4367 TRACE("%p, ver %d\n", template, version );
4368 switch (version)
4370 case 0: /* standard format is version of 0 */
4371 offset = GET_WORD(p);
4372 p += sizeof(WORD) + offset;
4373 if (!(hMenu = CreateMenu())) return 0;
4374 if (!MENU_ParseResource( p, hMenu, TRUE ))
4376 DestroyMenu( hMenu );
4377 return 0;
4379 return hMenu;
4380 case 1: /* extended format is version of 1 */
4381 offset = GET_WORD(p);
4382 p += sizeof(WORD) + offset;
4383 if (!(hMenu = CreateMenu())) return 0;
4384 if (!MENUEX_ParseResource( p, hMenu))
4386 DestroyMenu( hMenu );
4387 return 0;
4389 return hMenu;
4390 default:
4391 ERR("version %d not supported.\n", version);
4392 return 0;
4397 /**********************************************************************
4398 * LoadMenuIndirectA (USER32.@)
4400 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4402 return LoadMenuIndirectW( template );
4406 /**********************************************************************
4407 * IsMenu (USER32.@)
4409 BOOL WINAPI IsMenu(HMENU hmenu)
4411 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4413 if (!menu)
4415 SetLastError(ERROR_INVALID_MENU_HANDLE);
4416 return FALSE;
4418 return TRUE;
4421 /**********************************************************************
4422 * GetMenuItemInfo_common
4425 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4426 LPMENUITEMINFOW lpmii, BOOL unicode)
4428 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4430 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4432 if (!menu)
4433 return FALSE;
4435 if( lpmii->fMask & MIIM_TYPE) {
4436 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4437 WARN("invalid combination of fMask bits used\n");
4438 /* this does not happen on Win9x/ME */
4439 SetLastError( ERROR_INVALID_PARAMETER);
4440 return FALSE;
4442 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4443 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4444 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4445 if( lpmii->fType & MFT_BITMAP) {
4446 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4447 lpmii->cch = 0;
4448 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4449 /* this does not happen on Win9x/ME */
4450 lpmii->dwTypeData = 0;
4451 lpmii->cch = 0;
4455 /* copy the text string */
4456 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4457 if( !menu->text ) {
4458 if(lpmii->dwTypeData && lpmii->cch) {
4459 lpmii->cch = 0;
4460 if( unicode)
4461 *((WCHAR *)lpmii->dwTypeData) = 0;
4462 else
4463 *((CHAR *)lpmii->dwTypeData) = 0;
4465 } else {
4466 int len;
4467 if (unicode)
4469 len = strlenW(menu->text);
4470 if(lpmii->dwTypeData && lpmii->cch)
4471 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4473 else
4475 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4476 0, NULL, NULL ) - 1;
4477 if(lpmii->dwTypeData && lpmii->cch)
4478 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4479 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4480 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4482 /* if we've copied a substring we return its length */
4483 if(lpmii->dwTypeData && lpmii->cch)
4484 if (lpmii->cch <= len + 1)
4485 lpmii->cch--;
4486 else
4487 lpmii->cch = len;
4488 else {
4489 /* return length of string */
4490 /* not on Win9x/ME if fType & MFT_BITMAP */
4491 lpmii->cch = len;
4496 if (lpmii->fMask & MIIM_FTYPE)
4497 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4499 if (lpmii->fMask & MIIM_BITMAP)
4500 lpmii->hbmpItem = menu->hbmpItem;
4502 if (lpmii->fMask & MIIM_STATE)
4503 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4505 if (lpmii->fMask & MIIM_ID)
4506 lpmii->wID = menu->wID;
4508 if (lpmii->fMask & MIIM_SUBMENU)
4509 lpmii->hSubMenu = menu->hSubMenu;
4510 else {
4511 /* hSubMenu is always cleared
4512 * (not on Win9x/ME ) */
4513 lpmii->hSubMenu = 0;
4516 if (lpmii->fMask & MIIM_CHECKMARKS) {
4517 lpmii->hbmpChecked = menu->hCheckBit;
4518 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4520 if (lpmii->fMask & MIIM_DATA)
4521 lpmii->dwItemData = menu->dwItemData;
4523 return TRUE;
4526 /**********************************************************************
4527 * GetMenuItemInfoA (USER32.@)
4529 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4530 LPMENUITEMINFOA lpmii)
4532 BOOL ret;
4533 MENUITEMINFOA mii;
4534 if( lpmii->cbSize != sizeof( mii) &&
4535 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4536 SetLastError( ERROR_INVALID_PARAMETER);
4537 return FALSE;
4539 memcpy( &mii, lpmii, lpmii->cbSize);
4540 mii.cbSize = sizeof( mii);
4541 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4542 (LPMENUITEMINFOW)&mii, FALSE);
4543 mii.cbSize = lpmii->cbSize;
4544 memcpy( lpmii, &mii, mii.cbSize);
4545 return ret;
4548 /**********************************************************************
4549 * GetMenuItemInfoW (USER32.@)
4551 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4552 LPMENUITEMINFOW lpmii)
4554 BOOL ret;
4555 MENUITEMINFOW mii;
4556 if( lpmii->cbSize != sizeof( mii) &&
4557 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4558 SetLastError( ERROR_INVALID_PARAMETER);
4559 return FALSE;
4561 memcpy( &mii, lpmii, lpmii->cbSize);
4562 mii.cbSize = sizeof( mii);
4563 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4564 mii.cbSize = lpmii->cbSize;
4565 memcpy( lpmii, &mii, mii.cbSize);
4566 return ret;
4570 /* set a menu item text from a ASCII or Unicode string */
4571 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4573 if (!text)
4574 menu->text = NULL;
4575 else if (unicode)
4577 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4578 strcpyW( menu->text, text );
4580 else
4582 LPCSTR str = (LPCSTR)text;
4583 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4584 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4585 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4590 /**********************************************************************
4591 * SetMenuItemInfo_common
4594 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4595 const MENUITEMINFOW *lpmii,
4596 BOOL unicode)
4598 if (!menu) return FALSE;
4600 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4602 if (lpmii->fMask & MIIM_TYPE ) {
4603 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4604 WARN("invalid combination of fMask bits used\n");
4605 /* this does not happen on Win9x/ME */
4606 SetLastError( ERROR_INVALID_PARAMETER);
4607 return FALSE;
4610 /* Remove the old type bits and replace them with the new ones */
4611 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4612 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4614 if (IS_STRING_ITEM(menu->fType)) {
4615 HeapFree(GetProcessHeap(), 0, menu->text);
4616 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4617 } else if( (menu->fType) & MFT_BITMAP)
4618 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4621 if (lpmii->fMask & MIIM_FTYPE ) {
4622 if(( lpmii->fType & MFT_BITMAP)) {
4623 SetLastError( ERROR_INVALID_PARAMETER);
4624 return FALSE;
4626 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4627 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4629 if (lpmii->fMask & MIIM_STRING ) {
4630 /* free the string when used */
4631 HeapFree(GetProcessHeap(), 0, menu->text);
4632 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4635 if (lpmii->fMask & MIIM_STATE)
4637 /* Other menu items having MFS_DEFAULT are not converted
4638 to normal items */
4639 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4642 if (lpmii->fMask & MIIM_ID)
4643 menu->wID = lpmii->wID;
4645 if (lpmii->fMask & MIIM_SUBMENU) {
4646 menu->hSubMenu = lpmii->hSubMenu;
4647 if (menu->hSubMenu) {
4648 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4649 if (subMenu) {
4650 subMenu->wFlags |= MF_POPUP;
4651 menu->fType |= MF_POPUP;
4653 else {
4654 SetLastError( ERROR_INVALID_PARAMETER);
4655 return FALSE;
4658 else
4659 menu->fType &= ~MF_POPUP;
4662 if (lpmii->fMask & MIIM_CHECKMARKS)
4664 menu->hCheckBit = lpmii->hbmpChecked;
4665 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4667 if (lpmii->fMask & MIIM_DATA)
4668 menu->dwItemData = lpmii->dwItemData;
4670 if (lpmii->fMask & MIIM_BITMAP)
4671 menu->hbmpItem = lpmii->hbmpItem;
4673 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4674 menu->fType |= MFT_SEPARATOR;
4676 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4677 return TRUE;
4680 /**********************************************************************
4681 * SetMenuItemInfoA (USER32.@)
4683 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4684 const MENUITEMINFOA *lpmii)
4686 MENUITEMINFOA mii;
4688 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4690 if( lpmii->cbSize != sizeof( mii) &&
4691 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4692 SetLastError( ERROR_INVALID_PARAMETER);
4693 return FALSE;
4695 memcpy( &mii, lpmii, lpmii->cbSize);
4696 if( lpmii->cbSize != sizeof( mii)) {
4697 mii.cbSize = sizeof( mii);
4698 mii.hbmpItem = NULL;
4700 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4701 (const MENUITEMINFOW *)&mii, FALSE);
4704 /**********************************************************************
4705 * SetMenuItemInfoW (USER32.@)
4707 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4708 const MENUITEMINFOW *lpmii)
4710 MENUITEMINFOW mii;
4712 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4714 if( lpmii->cbSize != sizeof( mii) &&
4715 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4716 SetLastError( ERROR_INVALID_PARAMETER);
4717 return FALSE;
4719 memcpy( &mii, lpmii, lpmii->cbSize);
4720 if( lpmii->cbSize != sizeof( mii)) {
4721 mii.cbSize = sizeof( mii);
4722 mii.hbmpItem = NULL;
4724 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4725 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4728 /**********************************************************************
4729 * SetMenuDefaultItem (USER32.@)
4732 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4734 UINT i;
4735 POPUPMENU *menu;
4736 MENUITEM *item;
4738 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4740 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4742 /* reset all default-item flags */
4743 item = menu->items;
4744 for (i = 0; i < menu->nItems; i++, item++)
4746 item->fState &= ~MFS_DEFAULT;
4749 /* no default item */
4750 if ( -1 == uItem)
4752 return TRUE;
4755 item = menu->items;
4756 if ( bypos )
4758 if ( uItem >= menu->nItems ) return FALSE;
4759 item[uItem].fState |= MFS_DEFAULT;
4760 return TRUE;
4762 else
4764 for (i = 0; i < menu->nItems; i++, item++)
4766 if (item->wID == uItem)
4768 item->fState |= MFS_DEFAULT;
4769 return TRUE;
4774 return FALSE;
4777 /**********************************************************************
4778 * GetMenuDefaultItem (USER32.@)
4780 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4782 POPUPMENU *menu;
4783 MENUITEM * item;
4784 UINT i = 0;
4786 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4788 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4790 /* find default item */
4791 item = menu->items;
4793 /* empty menu */
4794 if (! item) return -1;
4796 while ( !( item->fState & MFS_DEFAULT ) )
4798 i++; item++;
4799 if (i >= menu->nItems ) return -1;
4802 /* default: don't return disabled items */
4803 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4805 /* search rekursiv when needed */
4806 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4808 UINT ret;
4809 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4810 if ( -1 != ret ) return ret;
4812 /* when item not found in submenu, return the popup item */
4814 return ( bypos ) ? i : item->wID;
4819 /**********************************************************************
4820 * InsertMenuItemA (USER32.@)
4822 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4823 const MENUITEMINFOA *lpmii)
4825 MENUITEM *item;
4826 MENUITEMINFOA mii;
4828 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4830 if( lpmii->cbSize != sizeof( mii) &&
4831 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4832 SetLastError( ERROR_INVALID_PARAMETER);
4833 return FALSE;
4835 memcpy( &mii, lpmii, lpmii->cbSize);
4836 if( lpmii->cbSize != sizeof( mii)) {
4837 mii.cbSize = sizeof( mii);
4838 mii.hbmpItem = NULL;
4841 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4842 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4846 /**********************************************************************
4847 * InsertMenuItemW (USER32.@)
4849 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4850 const MENUITEMINFOW *lpmii)
4852 MENUITEM *item;
4853 MENUITEMINFOW mii;
4855 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4857 if( lpmii->cbSize != sizeof( mii) &&
4858 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4859 SetLastError( ERROR_INVALID_PARAMETER);
4860 return FALSE;
4862 memcpy( &mii, lpmii, lpmii->cbSize);
4863 if( lpmii->cbSize != sizeof( mii)) {
4864 mii.cbSize = sizeof( mii);
4865 mii.hbmpItem = NULL;
4868 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4869 return SetMenuItemInfo_common(item, &mii, TRUE);
4872 /**********************************************************************
4873 * CheckMenuRadioItem (USER32.@)
4876 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4877 UINT first, UINT last, UINT check,
4878 UINT bypos)
4880 BOOL done = FALSE;
4881 UINT i;
4882 MENUITEM *mi_first = NULL, *mi_check;
4883 HMENU m_first, m_check;
4885 for (i = first; i <= last; i++)
4887 UINT pos = i;
4889 if (!mi_first)
4891 m_first = hMenu;
4892 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4893 if (!mi_first) continue;
4894 mi_check = mi_first;
4895 m_check = m_first;
4897 else
4899 m_check = hMenu;
4900 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4901 if (!mi_check) continue;
4904 if (m_first != m_check) continue;
4905 if (mi_check->fType == MFT_SEPARATOR) continue;
4907 if (i == check)
4909 mi_check->fType |= MFT_RADIOCHECK;
4910 mi_check->fState |= MFS_CHECKED;
4911 done = TRUE;
4913 else
4915 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4916 mi_check->fState &= ~MFS_CHECKED;
4920 return done;
4924 /**********************************************************************
4925 * GetMenuItemRect (USER32.@)
4927 * ATTENTION: Here, the returned values in rect are the screen
4928 * coordinates of the item just like if the menu was
4929 * always on the upper left side of the application.
4932 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4933 LPRECT rect)
4935 POPUPMENU *itemMenu;
4936 MENUITEM *item;
4937 HWND referenceHwnd;
4939 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4941 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4942 referenceHwnd = hwnd;
4944 if(!hwnd)
4946 itemMenu = MENU_GetMenu(hMenu);
4947 if (itemMenu == NULL)
4948 return FALSE;
4950 if(itemMenu->hWnd == 0)
4951 return FALSE;
4952 referenceHwnd = itemMenu->hWnd;
4955 if ((rect == NULL) || (item == NULL))
4956 return FALSE;
4958 *rect = item->rect;
4960 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4962 return TRUE;
4966 /**********************************************************************
4967 * SetMenuInfo (USER32.@)
4969 * FIXME
4970 * MIM_APPLYTOSUBMENUS
4971 * actually use the items to draw the menu
4973 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4975 POPUPMENU *menu;
4977 TRACE("(%p %p)\n", hMenu, lpmi);
4979 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4982 if (lpmi->fMask & MIM_BACKGROUND)
4983 menu->hbrBack = lpmi->hbrBack;
4985 if (lpmi->fMask & MIM_HELPID)
4986 menu->dwContextHelpID = lpmi->dwContextHelpID;
4988 if (lpmi->fMask & MIM_MAXHEIGHT)
4989 menu->cyMax = lpmi->cyMax;
4991 if (lpmi->fMask & MIM_MENUDATA)
4992 menu->dwMenuData = lpmi->dwMenuData;
4994 if (lpmi->fMask & MIM_STYLE)
4996 menu->dwStyle = lpmi->dwStyle;
4997 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4998 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4999 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5000 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
5003 return TRUE;
5005 return FALSE;
5008 /**********************************************************************
5009 * GetMenuInfo (USER32.@)
5011 * NOTES
5012 * win98/NT5.0
5015 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5016 { POPUPMENU *menu;
5018 TRACE("(%p %p)\n", hMenu, lpmi);
5020 if (lpmi && (menu = MENU_GetMenu(hMenu)))
5023 if (lpmi->fMask & MIM_BACKGROUND)
5024 lpmi->hbrBack = menu->hbrBack;
5026 if (lpmi->fMask & MIM_HELPID)
5027 lpmi->dwContextHelpID = menu->dwContextHelpID;
5029 if (lpmi->fMask & MIM_MAXHEIGHT)
5030 lpmi->cyMax = menu->cyMax;
5032 if (lpmi->fMask & MIM_MENUDATA)
5033 lpmi->dwMenuData = menu->dwMenuData;
5035 if (lpmi->fMask & MIM_STYLE)
5036 lpmi->dwStyle = menu->dwStyle;
5038 return TRUE;
5040 return FALSE;
5044 /**********************************************************************
5045 * SetMenuContextHelpId (USER32.@)
5047 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5049 LPPOPUPMENU menu;
5051 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5053 if ((menu = MENU_GetMenu(hMenu)))
5055 menu->dwContextHelpID = dwContextHelpID;
5056 return TRUE;
5058 return FALSE;
5062 /**********************************************************************
5063 * GetMenuContextHelpId (USER32.@)
5065 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5067 LPPOPUPMENU menu;
5069 TRACE("(%p)\n", hMenu);
5071 if ((menu = MENU_GetMenu(hMenu)))
5073 return menu->dwContextHelpID;
5075 return 0;
5078 /**********************************************************************
5079 * MenuItemFromPoint (USER32.@)
5081 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5083 POPUPMENU *menu = MENU_GetMenu(hMenu);
5084 UINT pos;
5086 /*FIXME: Do we have to handle hWnd here? */
5087 if (!menu) return -1;
5088 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5089 return pos;
5093 /**********************************************************************
5094 * translate_accelerator
5096 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5097 BYTE fVirt, WORD key, WORD cmd )
5099 INT mask = 0;
5100 UINT mesg = 0;
5102 if (wParam != key) return FALSE;
5104 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5105 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5106 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5108 if (message == WM_CHAR || message == WM_SYSCHAR)
5110 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5112 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5113 goto found;
5116 else
5118 if(fVirt & FVIRTKEY)
5120 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5121 wParam, 0xff & HIWORD(lParam));
5123 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5124 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5126 else
5128 if (!(lParam & 0x01000000)) /* no special_key */
5130 if ((fVirt & FALT) && (lParam & 0x20000000))
5131 { /* ^^ ALT pressed */
5132 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5133 goto found;
5138 return FALSE;
5140 found:
5141 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5142 mesg = 1;
5143 else
5145 HMENU hMenu, hSubMenu, hSysMenu;
5146 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5148 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5149 hSysMenu = get_win_sys_menu( hWnd );
5151 /* find menu item and ask application to initialize it */
5152 /* 1. in the system menu */
5153 hSubMenu = hSysMenu;
5154 nPos = cmd;
5155 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5157 if (GetCapture())
5158 mesg = 2;
5159 if (!IsWindowEnabled(hWnd))
5160 mesg = 3;
5161 else
5163 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5164 if(hSubMenu != hSysMenu)
5166 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5167 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5168 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5170 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5173 else /* 2. in the window's menu */
5175 hSubMenu = hMenu;
5176 nPos = cmd;
5177 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5179 if (GetCapture())
5180 mesg = 2;
5181 if (!IsWindowEnabled(hWnd))
5182 mesg = 3;
5183 else
5185 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5186 if(hSubMenu != hMenu)
5188 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5189 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5190 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5192 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5197 if (mesg == 0)
5199 if (uSysStat != (UINT)-1)
5201 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5202 mesg=4;
5203 else
5204 mesg=WM_SYSCOMMAND;
5206 else
5208 if (uStat != (UINT)-1)
5210 if (IsIconic(hWnd))
5211 mesg=5;
5212 else
5214 if (uStat & (MF_DISABLED|MF_GRAYED))
5215 mesg=6;
5216 else
5217 mesg=WM_COMMAND;
5220 else
5221 mesg=WM_COMMAND;
5226 if( mesg==WM_COMMAND )
5228 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5229 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5231 else if( mesg==WM_SYSCOMMAND )
5233 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5234 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5236 else
5238 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5239 * #0: unknown (please report!)
5240 * #1: for WM_KEYUP,WM_SYSKEYUP
5241 * #2: mouse is captured
5242 * #3: window is disabled
5243 * #4: it's a disabled system menu option
5244 * #5: it's a menu option, but window is iconic
5245 * #6: it's a menu option, but disabled
5247 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5248 if(mesg==0)
5249 ERR_(accel)(" unknown reason - please report!\n");
5251 return TRUE;
5254 /**********************************************************************
5255 * TranslateAcceleratorA (USER32.@)
5256 * TranslateAccelerator (USER32.@)
5258 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5260 /* YES, Accel16! */
5261 LPACCEL16 lpAccelTbl;
5262 int i;
5263 WPARAM wParam;
5265 if (!hWnd || !msg) return 0;
5267 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5269 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5270 return 0;
5273 wParam = msg->wParam;
5275 switch (msg->message)
5277 case WM_KEYDOWN:
5278 case WM_SYSKEYDOWN:
5279 break;
5281 case WM_CHAR:
5282 case WM_SYSCHAR:
5284 char ch = LOWORD(wParam);
5285 WCHAR wch;
5286 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5287 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5289 break;
5291 default:
5292 return 0;
5295 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5296 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5297 i = 0;
5300 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5301 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5302 return 1;
5303 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5305 return 0;
5308 /**********************************************************************
5309 * TranslateAcceleratorW (USER32.@)
5311 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5313 /* YES, Accel16! */
5314 LPACCEL16 lpAccelTbl;
5315 int i;
5317 if (!hWnd || !msg) return 0;
5319 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5321 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5322 return 0;
5325 switch (msg->message)
5327 case WM_KEYDOWN:
5328 case WM_SYSKEYDOWN:
5329 case WM_CHAR:
5330 case WM_SYSCHAR:
5331 break;
5333 default:
5334 return 0;
5337 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5338 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5339 i = 0;
5342 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5343 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5344 return 1;
5345 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5347 return 0;