user32: Fix some compilation warnings in 64-bit mode.
[wine/wine-kai.git] / dlls / user32 / menu.c
blob692a70cb9bf9322268002e97162e135c3558928e
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 (LPCSTR)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, 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 wrect;
707 RECT rect;
709 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
710 pt.x -= wrect.left;pt.y -= wrect.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 ((pt.x >= rect.left) && (pt.x < rect.right) &&
717 (pt.y >= rect.top) && (pt.y < rect.bottom))
719 if (pos) *pos = i;
720 return item;
723 return NULL;
727 /***********************************************************************
728 * MENU_FindItemByKey
730 * Find the menu item selected by a key press.
731 * Return item id, -1 if none, -2 if we should close the menu.
733 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
734 WCHAR key, BOOL forceMenuChar )
736 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
738 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
740 if (hmenu)
742 POPUPMENU *menu = MENU_GetMenu( hmenu );
743 MENUITEM *item = menu->items;
744 LRESULT menuchar;
746 if( !forceMenuChar )
748 UINT i;
750 for (i = 0; i < menu->nItems; i++, item++)
752 if( item->text)
754 WCHAR *p = item->text - 2;
757 p = strchrW (p + 2, '&');
759 while (p != NULL && p [1] == '&');
760 if (p && (toupperW(p[1]) == toupperW(key))) return i;
764 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
765 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
766 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
767 if (HIWORD(menuchar) == 1) return (UINT)(-2);
769 return (UINT)(-1);
773 /***********************************************************************
774 * MENU_GetBitmapItemSize
776 * Get the size of a bitmap item.
778 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
779 HWND hwndOwner)
781 BITMAP bm;
782 HBITMAP bmp = lpitem->hbmpItem;
784 size->cx = size->cy = 0;
786 /* check if there is a magic menu item associated with this item */
787 switch( (INT_PTR) bmp )
789 case (INT_PTR)HBMMENU_CALLBACK:
791 MEASUREITEMSTRUCT measItem;
792 measItem.CtlType = ODT_MENU;
793 measItem.CtlID = 0;
794 measItem.itemID = lpitem->wID;
795 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
796 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
797 measItem.itemData = lpitem->dwItemData;
798 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
799 size->cx = measItem.itemWidth;
800 size->cy = measItem.itemHeight;
801 return;
803 break;
804 case (INT_PTR)HBMMENU_SYSTEM:
805 if (lpitem->dwItemData)
807 bmp = (HBITMAP)lpitem->dwItemData;
808 break;
810 /* fall through */
811 case (INT_PTR)HBMMENU_MBAR_RESTORE:
812 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
813 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
814 case (INT_PTR)HBMMENU_MBAR_CLOSE:
815 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
816 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
817 size->cy = size->cx;
818 return;
819 case (INT_PTR)HBMMENU_POPUP_CLOSE:
820 case (INT_PTR)HBMMENU_POPUP_RESTORE:
821 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
822 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
823 FIXME("Magic %p not implemented\n", bmp );
824 return;
826 if (GetObjectW(bmp, sizeof(bm), &bm ))
828 size->cx = bm.bmWidth;
829 size->cy = bm.bmHeight;
833 /***********************************************************************
834 * MENU_DrawBitmapItem
836 * Draw a bitmap item.
838 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
839 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
841 BITMAP bm;
842 DWORD rop;
843 HDC hdcMem;
844 HBITMAP bmp;
845 int w = rect->right - rect->left;
846 int h = rect->bottom - rect->top;
847 int bmp_xoffset = 0;
848 int left, top;
849 HBITMAP hbmToDraw = lpitem->hbmpItem;
850 bmp = hbmToDraw;
852 /* Check if there is a magic menu item associated with this item */
853 if (IS_MAGIC_BITMAP(hbmToDraw))
855 UINT flags = 0;
856 RECT r;
858 switch((INT_PTR)hbmToDraw)
860 case (INT_PTR)HBMMENU_SYSTEM:
861 if (lpitem->dwItemData)
863 bmp = (HBITMAP)lpitem->dwItemData;
864 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
866 else
868 static HBITMAP hBmpSysMenu;
870 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
871 bmp = hBmpSysMenu;
872 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
873 /* only use right half of the bitmap */
874 bmp_xoffset = bm.bmWidth / 2;
875 bm.bmWidth -= bmp_xoffset;
877 goto got_bitmap;
878 case (INT_PTR)HBMMENU_MBAR_RESTORE:
879 flags = DFCS_CAPTIONRESTORE;
880 break;
881 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
882 flags = DFCS_CAPTIONMIN;
883 break;
884 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
885 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
886 break;
887 case (INT_PTR)HBMMENU_MBAR_CLOSE:
888 flags = DFCS_CAPTIONCLOSE;
889 break;
890 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
891 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
892 break;
893 case (INT_PTR)HBMMENU_CALLBACK:
895 DRAWITEMSTRUCT drawItem;
896 drawItem.CtlType = ODT_MENU;
897 drawItem.CtlID = 0;
898 drawItem.itemID = lpitem->wID;
899 drawItem.itemAction = odaction;
900 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
901 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
902 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
903 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
904 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
905 drawItem.hwndItem = (HWND)hmenu;
906 drawItem.hDC = hdc;
907 drawItem.itemData = lpitem->dwItemData;
908 drawItem.rcItem = *rect;
909 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
910 return;
912 break;
913 case (INT_PTR)HBMMENU_POPUP_CLOSE:
914 case (INT_PTR)HBMMENU_POPUP_RESTORE:
915 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
916 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
917 default:
918 FIXME("Magic %p not implemented\n", hbmToDraw);
919 return;
921 r = *rect;
922 InflateRect( &r, -1, -1 );
923 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
924 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
925 return;
928 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
930 got_bitmap:
931 hdcMem = CreateCompatibleDC( hdc );
932 SelectObject( hdcMem, bmp );
934 /* handle fontsize > bitmap_height */
935 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
936 left=rect->left;
937 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
938 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
939 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
940 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
941 DeleteDC( hdcMem );
945 /***********************************************************************
946 * MENU_CalcItemSize
948 * Calculate the size of the menu item and store it in lpitem->rect.
950 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
951 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
953 WCHAR *p;
954 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
955 UINT arrow_bitmap_width;
956 BITMAP bm;
957 INT itemheight;
959 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
960 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
961 (menuBar ? " (MenuBar)" : ""));
963 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
964 arrow_bitmap_width = bm.bmWidth;
966 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
967 if( !menucharsize.cx ) {
968 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
969 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
970 * but it is unlikely an application will depend on that */
971 ODitemheight = HIWORD( GetDialogBaseUnits());
974 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
976 if (lpitem->fType & MF_OWNERDRAW)
978 MEASUREITEMSTRUCT mis;
979 mis.CtlType = ODT_MENU;
980 mis.CtlID = 0;
981 mis.itemID = lpitem->wID;
982 mis.itemData = lpitem->dwItemData;
983 mis.itemHeight = ODitemheight;
984 mis.itemWidth = 0;
985 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
986 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
987 * width of a menufont character to the width of an owner-drawn menu.
989 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
990 if (menuBar) {
991 /* under at least win95 you seem to be given a standard
992 height for the menu and the height value is ignored */
993 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
994 } else
995 lpitem->rect.bottom += mis.itemHeight;
997 TRACE("id=%04lx size=%dx%d\n",
998 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
999 lpitem->rect.bottom-lpitem->rect.top);
1000 return;
1003 if (lpitem->fType & MF_SEPARATOR)
1005 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1006 if( !menuBar)
1007 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1008 return;
1011 itemheight = 0;
1012 lpitem->xTab = 0;
1014 if (!menuBar) {
1015 if (lpitem->hbmpItem) {
1016 SIZE size;
1018 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1019 /* Keep the size of the bitmap in callback mode to be able
1020 * to draw it correctly */
1021 lpitem->bmpsize = size;
1022 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1023 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1024 lpitem->rect.right += size.cx + 2;
1025 itemheight = size.cy + 2;
1027 if( !(lppop->dwStyle & MNS_NOCHECK))
1028 lpitem->rect.right += check_bitmap_width;
1029 lpitem->rect.right += 4 + menucharsize.cx;
1030 lpitem->xTab = lpitem->rect.right;
1031 lpitem->rect.right += arrow_bitmap_width;
1032 } else if (lpitem->hbmpItem) { /* menuBar */
1033 SIZE size;
1035 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1036 lpitem->bmpsize = size;
1037 lpitem->rect.right += size.cx;
1038 if( lpitem->text) lpitem->rect.right += 2;
1039 itemheight = size.cy;
1042 /* it must be a text item - unless it's the system menu */
1043 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1044 HFONT hfontOld = NULL;
1045 RECT rc = lpitem->rect;
1046 LONG txtheight, txtwidth;
1048 if ( lpitem->fState & MFS_DEFAULT ) {
1049 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1051 if (menuBar) {
1052 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1053 DT_SINGLELINE|DT_CALCRECT);
1054 lpitem->rect.right += rc.right - rc.left;
1055 itemheight = max( max( itemheight, txtheight),
1056 GetSystemMetrics( SM_CYMENU) - 1);
1057 lpitem->rect.right += 2 * menucharsize.cx;
1058 } else {
1059 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1060 RECT tmprc = rc;
1061 LONG tmpheight;
1062 int n = (int)( p - lpitem->text);
1063 /* Item contains a tab (only meaningful in popup menus) */
1064 /* get text size before the tab */
1065 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1066 DT_SINGLELINE|DT_CALCRECT);
1067 txtwidth = rc.right - rc.left;
1068 p += 1; /* advance past the Tab */
1069 /* get text size after the tab */
1070 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1071 DT_SINGLELINE|DT_CALCRECT);
1072 lpitem->xTab += txtwidth;
1073 txtheight = max( txtheight, tmpheight);
1074 txtwidth += menucharsize.cx + /* space for the tab */
1075 tmprc.right - tmprc.left; /* space for the short cut */
1076 } else {
1077 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1078 DT_SINGLELINE|DT_CALCRECT);
1079 txtwidth = rc.right - rc.left;
1080 lpitem->xTab += txtwidth;
1082 lpitem->rect.right += 2 + txtwidth;
1083 itemheight = max( itemheight,
1084 max( txtheight + 2, menucharsize.cy + 4));
1086 if (hfontOld) SelectObject (hdc, hfontOld);
1087 } else if( menuBar) {
1088 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1090 lpitem->rect.bottom += itemheight;
1091 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1095 /***********************************************************************
1096 * MENU_GetMaxPopupHeight
1098 static UINT
1099 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1101 if (lppop->cyMax)
1102 return lppop->cyMax;
1103 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1107 /***********************************************************************
1108 * MENU_PopupMenuCalcSize
1110 * Calculate the size of a popup menu.
1112 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1114 MENUITEM *lpitem;
1115 HDC hdc;
1116 int start, i;
1117 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1119 lppop->Width = lppop->Height = 0;
1120 if (lppop->nItems == 0) return;
1121 hdc = GetDC( 0 );
1123 SelectObject( hdc, get_menu_font(FALSE));
1125 start = 0;
1126 maxX = 2 + 1;
1128 lppop->maxBmpSize.cx = 0;
1129 lppop->maxBmpSize.cy = 0;
1131 while (start < lppop->nItems)
1133 lpitem = &lppop->items[start];
1134 orgX = maxX;
1135 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1136 orgX += MENU_COL_SPACE;
1137 orgY = MENU_TOP_MARGIN;
1139 maxTab = maxTabWidth = 0;
1140 /* Parse items until column break or end of menu */
1141 for (i = start; i < lppop->nItems; i++, lpitem++)
1143 if ((i != start) &&
1144 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1146 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1147 maxX = max( maxX, lpitem->rect.right );
1148 orgY = lpitem->rect.bottom;
1149 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1151 maxTab = max( maxTab, lpitem->xTab );
1152 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1156 /* Finish the column (set all items to the largest width found) */
1157 maxX = max( maxX, maxTab + maxTabWidth );
1158 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1160 lpitem->rect.right = maxX;
1161 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1162 lpitem->xTab = maxTab;
1165 lppop->Height = max( lppop->Height, orgY );
1168 lppop->Width = maxX;
1170 /* space for 3d border */
1171 lppop->Height += MENU_BOTTOM_MARGIN;
1172 lppop->Width += 2;
1174 /* Adjust popup height if it exceeds maximum */
1175 maxHeight = MENU_GetMaxPopupHeight(lppop);
1176 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1177 if (lppop->Height >= maxHeight)
1179 lppop->Height = maxHeight;
1180 lppop->bScrolling = TRUE;
1182 else
1184 lppop->bScrolling = FALSE;
1187 ReleaseDC( 0, hdc );
1191 /***********************************************************************
1192 * MENU_MenuBarCalcSize
1194 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1195 * height is off by 1 pixel which causes lengthy window relocations when
1196 * active document window is maximized/restored.
1198 * Calculate the size of the menu bar.
1200 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1201 LPPOPUPMENU lppop, HWND hwndOwner )
1203 MENUITEM *lpitem;
1204 int start, i, orgX, orgY, maxY, helpPos;
1206 if ((lprect == NULL) || (lppop == NULL)) return;
1207 if (lppop->nItems == 0) return;
1208 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1209 lppop->Width = lprect->right - lprect->left;
1210 lppop->Height = 0;
1211 maxY = lprect->top+1;
1212 start = 0;
1213 helpPos = -1;
1214 lppop->maxBmpSize.cx = 0;
1215 lppop->maxBmpSize.cy = 0;
1216 while (start < lppop->nItems)
1218 lpitem = &lppop->items[start];
1219 orgX = lprect->left;
1220 orgY = maxY;
1222 /* Parse items until line break or end of menu */
1223 for (i = start; i < lppop->nItems; i++, lpitem++)
1225 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1226 if ((i != start) &&
1227 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1229 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1230 debug_print_menuitem (" item: ", lpitem, "");
1231 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1233 if (lpitem->rect.right > lprect->right)
1235 if (i != start) break;
1236 else lpitem->rect.right = lprect->right;
1238 maxY = max( maxY, lpitem->rect.bottom );
1239 orgX = lpitem->rect.right;
1242 /* Finish the line (set all items to the largest height found) */
1243 while (start < i) lppop->items[start++].rect.bottom = maxY;
1246 lprect->bottom = maxY;
1247 lppop->Height = lprect->bottom - lprect->top;
1249 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1250 /* the last item (if several lines, only move the last line) */
1251 lpitem = &lppop->items[lppop->nItems-1];
1252 orgY = lpitem->rect.top;
1253 orgX = lprect->right;
1254 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1255 if ( (helpPos==-1) || (helpPos>i) )
1256 break; /* done */
1257 if (lpitem->rect.top != orgY) break; /* Other line */
1258 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1259 lpitem->rect.left += orgX - lpitem->rect.right;
1260 lpitem->rect.right = orgX;
1261 orgX = lpitem->rect.left;
1266 /***********************************************************************
1267 * MENU_DrawScrollArrows
1269 * Draw scroll arrows.
1271 static void
1272 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1274 HDC hdcMem = CreateCompatibleDC(hdc);
1275 HBITMAP hOrigBitmap;
1276 UINT arrow_bitmap_width, arrow_bitmap_height;
1277 BITMAP bmp;
1278 RECT rect;
1280 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1281 arrow_bitmap_width = bmp.bmWidth;
1282 arrow_bitmap_height = bmp.bmHeight;
1285 if (lppop->nScrollPos)
1286 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1287 else
1288 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1289 rect.left = 0;
1290 rect.top = 0;
1291 rect.right = lppop->Width;
1292 rect.bottom = arrow_bitmap_height;
1293 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1294 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1295 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1296 rect.top = lppop->Height - arrow_bitmap_height;
1297 rect.bottom = lppop->Height;
1298 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1299 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1300 SelectObject(hdcMem, get_down_arrow_bitmap());
1301 else
1302 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1303 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1304 lppop->Height - arrow_bitmap_height,
1305 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1306 SelectObject(hdcMem, hOrigBitmap);
1307 DeleteDC(hdcMem);
1311 /***********************************************************************
1312 * draw_popup_arrow
1314 * Draws the popup-menu arrow.
1316 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1317 UINT arrow_bitmap_height)
1319 HDC hdcMem = CreateCompatibleDC( hdc );
1320 HBITMAP hOrigBitmap;
1322 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1323 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1324 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1325 arrow_bitmap_width, arrow_bitmap_height,
1326 hdcMem, 0, 0, SRCCOPY );
1327 SelectObject( hdcMem, hOrigBitmap );
1328 DeleteDC( hdcMem );
1330 /***********************************************************************
1331 * MENU_DrawMenuItem
1333 * Draw a single menu item.
1335 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1336 UINT height, BOOL menuBar, UINT odaction )
1338 RECT rect;
1339 BOOL flat_menu = FALSE;
1340 int bkgnd;
1341 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1342 POPUPMENU *menu = MENU_GetMenu(hmenu);
1343 RECT bmprc;
1345 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1347 if (!menuBar) {
1348 BITMAP bmp;
1349 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1350 arrow_bitmap_width = bmp.bmWidth;
1351 arrow_bitmap_height = bmp.bmHeight;
1354 if (lpitem->fType & MF_SYSMENU)
1356 if( !IsIconic(hwnd) )
1357 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1358 return;
1361 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1362 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1364 /* Setup colors */
1366 if (lpitem->fState & MF_HILITE)
1368 if(menuBar && !flat_menu) {
1369 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1370 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1371 } else {
1372 if(lpitem->fState & MF_GRAYED)
1373 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1374 else
1375 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1376 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1379 else
1381 if (lpitem->fState & MF_GRAYED)
1382 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1383 else
1384 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1385 SetBkColor( hdc, GetSysColor( bkgnd ) );
1388 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1389 rect = lpitem->rect;
1390 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1392 if (lpitem->fType & MF_OWNERDRAW)
1395 ** Experimentation under Windows reveals that an owner-drawn
1396 ** menu is given the rectangle which includes the space it requested
1397 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1398 ** and a popup-menu arrow. This is the value of lpitem->rect.
1399 ** Windows will leave all drawing to the application except for
1400 ** the popup-menu arrow. Windows always draws that itself, after
1401 ** the menu owner has finished drawing.
1403 DRAWITEMSTRUCT dis;
1405 dis.CtlType = ODT_MENU;
1406 dis.CtlID = 0;
1407 dis.itemID = lpitem->wID;
1408 dis.itemData = lpitem->dwItemData;
1409 dis.itemState = 0;
1410 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1411 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1412 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1413 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1414 dis.hwndItem = (HWND)hmenu;
1415 dis.hDC = hdc;
1416 dis.rcItem = rect;
1417 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1418 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1419 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1420 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1421 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1422 /* Draw the popup-menu arrow */
1423 if (lpitem->fType & MF_POPUP)
1424 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1425 arrow_bitmap_height);
1426 return;
1429 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1431 if (lpitem->fState & MF_HILITE)
1433 if (flat_menu)
1435 InflateRect (&rect, -1, -1);
1436 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1437 InflateRect (&rect, 1, 1);
1438 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1440 else
1442 if(menuBar)
1443 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1444 else
1445 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1448 else
1449 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1451 SetBkMode( hdc, TRANSPARENT );
1453 /* vertical separator */
1454 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1456 HPEN oldPen;
1457 RECT rc = rect;
1459 rc.left -= MENU_COL_SPACE / 2 + 1;
1460 rc.top = 3;
1461 rc.bottom = height - 3;
1462 if (flat_menu)
1464 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1465 MoveToEx( hdc, rc.left, rc.top, NULL );
1466 LineTo( hdc, rc.left, rc.bottom );
1467 SelectObject( hdc, oldPen );
1469 else
1470 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1473 /* horizontal separator */
1474 if (lpitem->fType & MF_SEPARATOR)
1476 HPEN oldPen;
1477 RECT rc = rect;
1479 rc.left++;
1480 rc.right--;
1481 rc.top = ( rc.top + rc.bottom) / 2;
1482 if (flat_menu)
1484 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1485 MoveToEx( hdc, rc.left, rc.top, NULL );
1486 LineTo( hdc, rc.right, rc.top );
1487 SelectObject( hdc, oldPen );
1489 else
1490 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1491 return;
1494 /* helper lines for debugging */
1495 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1496 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1497 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1498 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1501 if (lpitem->hbmpItem) {
1502 /* calculate the bitmap rectangle in coordinates relative
1503 * to the item rectangle */
1504 if( menuBar) {
1505 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1506 bmprc.left = 3;
1507 else
1508 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1509 } else {
1510 bmprc.left = 4;
1511 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1512 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1514 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1515 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1516 bmprc.top = 0;
1517 else
1518 bmprc.top = (rect.bottom - rect.top -
1519 lpitem->bmpsize.cy) / 2;
1520 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1523 if (!menuBar)
1525 HBITMAP bm;
1526 INT y = rect.top + rect.bottom;
1527 RECT rc = rect;
1528 int checked = FALSE;
1529 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1530 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1531 /* Draw the check mark
1533 * FIXME:
1534 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1536 if( !(menu->dwStyle & MNS_NOCHECK)) {
1537 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1538 lpitem->hUnCheckBit;
1539 if (bm) /* we have a custom bitmap */
1541 HDC hdcMem = CreateCompatibleDC( hdc );
1543 SelectObject( hdcMem, bm );
1544 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1545 check_bitmap_width, check_bitmap_height,
1546 hdcMem, 0, 0, SRCCOPY );
1547 DeleteDC( hdcMem );
1548 checked = TRUE;
1550 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1552 RECT r;
1553 HBITMAP bm = CreateBitmap( check_bitmap_width,
1554 check_bitmap_height, 1, 1, NULL );
1555 HDC hdcMem = CreateCompatibleDC( hdc );
1557 SelectObject( hdcMem, bm );
1558 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1559 DrawFrameControl( hdcMem, &r, DFC_MENU,
1560 (lpitem->fType & MFT_RADIOCHECK) ?
1561 DFCS_MENUBULLET : DFCS_MENUCHECK );
1562 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1563 hdcMem, 0, 0, SRCCOPY );
1564 DeleteDC( hdcMem );
1565 DeleteObject( bm );
1566 checked = TRUE;
1569 if( lpitem->hbmpItem &&
1570 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1571 POINT origorg;
1572 /* some applications make this assumption on the DC's origin */
1573 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1574 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1575 odaction, FALSE);
1576 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1578 /* Draw the popup-menu arrow */
1579 if (lpitem->fType & MF_POPUP)
1580 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1581 arrow_bitmap_height);
1582 rect.left += 4;
1583 if( !(menu->dwStyle & MNS_NOCHECK))
1584 rect.left += check_bitmap_width;
1585 rect.right -= arrow_bitmap_width;
1587 else if( lpitem->hbmpItem)
1588 { /* Draw the bitmap */
1589 POINT origorg;
1591 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1592 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1593 odaction, menuBar);
1594 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1596 /* process text if present */
1597 if (lpitem->text)
1599 register int i;
1600 HFONT hfontOld = 0;
1602 UINT uFormat = (menuBar) ?
1603 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1604 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1606 if( !(menu->dwStyle & MNS_CHECKORBMP))
1607 rect.left += menu->maxBmpSize.cx;
1609 if ( lpitem->fState & MFS_DEFAULT )
1611 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1614 if (menuBar) {
1615 if( lpitem->hbmpItem)
1616 rect.left += lpitem->bmpsize.cx;
1617 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1618 rect.left += menucharsize.cx;
1619 rect.right -= menucharsize.cx;
1622 for (i = 0; lpitem->text[i]; i++)
1623 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1624 break;
1626 if(lpitem->fState & MF_GRAYED)
1628 if (!(lpitem->fState & MF_HILITE) )
1630 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1631 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1632 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1633 --rect.left; --rect.top; --rect.right; --rect.bottom;
1635 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1638 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1640 /* paint the shortcut text */
1641 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1643 if (lpitem->text[i] == '\t')
1645 rect.left = lpitem->xTab;
1646 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1648 else
1650 rect.right = lpitem->xTab;
1651 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1654 if(lpitem->fState & MF_GRAYED)
1656 if (!(lpitem->fState & MF_HILITE) )
1658 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1659 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1660 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1661 --rect.left; --rect.top; --rect.right; --rect.bottom;
1663 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1665 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1668 if (hfontOld)
1669 SelectObject (hdc, hfontOld);
1674 /***********************************************************************
1675 * MENU_DrawPopupMenu
1677 * Paint a popup menu.
1679 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1681 HBRUSH hPrevBrush = 0;
1682 RECT rect;
1684 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1686 GetClientRect( hwnd, &rect );
1688 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1689 && (SelectObject( hdc, get_menu_font(FALSE))))
1691 HPEN hPrevPen;
1693 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1695 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1696 if( hPrevPen )
1698 POPUPMENU *menu;
1699 BOOL flat_menu = FALSE;
1701 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1702 if (flat_menu)
1703 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1704 else
1705 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1707 if( (menu = MENU_GetMenu( hmenu )))
1709 /* draw menu items */
1710 if( menu->nItems)
1712 MENUITEM *item;
1713 UINT u;
1715 item = menu->items;
1716 for( u = menu->nItems; u > 0; u--, item++)
1717 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1718 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1720 /* draw scroll arrows */
1721 if (menu->bScrolling)
1722 MENU_DrawScrollArrows(menu, hdc);
1724 } else
1726 SelectObject( hdc, hPrevBrush );
1731 /***********************************************************************
1732 * MENU_DrawMenuBar
1734 * Paint a menu bar. Returns the height of the menu bar.
1735 * called from [windows/nonclient.c]
1737 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1738 BOOL suppress_draw)
1740 LPPOPUPMENU lppop;
1741 HFONT hfontOld = 0;
1742 HMENU hMenu = GetMenu(hwnd);
1744 lppop = MENU_GetMenu( hMenu );
1745 if (lppop == NULL || lprect == NULL)
1747 return GetSystemMetrics(SM_CYMENU);
1750 if (suppress_draw)
1752 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1754 if (lppop->Height == 0)
1755 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1757 lprect->bottom = lprect->top + lppop->Height;
1759 if (hfontOld) SelectObject( hDC, hfontOld);
1760 return lppop->Height;
1762 else
1763 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1767 /***********************************************************************
1768 * MENU_ShowPopup
1770 * Display a popup menu.
1772 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1773 INT x, INT y, INT xanchor, INT yanchor )
1775 POPUPMENU *menu;
1776 INT width, height;
1777 POINT pt;
1778 HMONITOR monitor;
1779 MONITORINFO info;
1781 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1782 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1784 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1785 if (menu->FocusedItem != NO_SELECTED_ITEM)
1787 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1788 menu->FocusedItem = NO_SELECTED_ITEM;
1791 /* store the owner for DrawItem */
1792 menu->hwndOwner = hwndOwner;
1794 menu->nScrollPos = 0;
1795 MENU_PopupMenuCalcSize( menu, hwndOwner );
1797 /* adjust popup menu pos so that it fits within the desktop */
1799 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1800 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1802 /* FIXME: should use item rect */
1803 pt.x = x;
1804 pt.y = y;
1805 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1806 info.cbSize = sizeof(info);
1807 GetMonitorInfoW( monitor, &info );
1808 if( x + width > info.rcWork.right)
1810 if( xanchor && x >= width - xanchor )
1811 x -= width - xanchor;
1813 if( x + width > info.rcWork.right)
1814 x = info.rcWork.right - width;
1816 if( x < info.rcWork.left ) x = info.rcWork.left;
1818 if( y + height > info.rcWork.bottom)
1820 if( yanchor && y >= height + yanchor )
1821 y -= height + yanchor;
1823 if( y + height > info.rcWork.bottom)
1824 y = info.rcWork.bottom - height;
1826 if( y < info.rcWork.top ) y = info.rcWork.top;
1828 /* NOTE: In Windows, top menu popup is not owned. */
1829 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1830 WS_POPUP, x, y, width, height,
1831 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1832 (LPVOID)hmenu );
1833 if( !menu->hWnd ) return FALSE;
1834 if (!top_popup) top_popup = menu->hWnd;
1836 /* Display the window */
1838 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1839 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1840 UpdateWindow( menu->hWnd );
1841 return TRUE;
1845 /***********************************************************************
1846 * MENU_EnsureMenuItemVisible
1848 static void
1849 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1851 if (lppop->bScrolling)
1853 MENUITEM *item = &lppop->items[wIndex];
1854 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1855 UINT nOldPos = lppop->nScrollPos;
1856 RECT rc;
1857 UINT arrow_bitmap_height;
1858 BITMAP bmp;
1860 GetClientRect(lppop->hWnd, &rc);
1862 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1863 arrow_bitmap_height = bmp.bmHeight;
1865 rc.top += arrow_bitmap_height;
1866 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1868 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1869 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1872 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1873 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1874 MENU_DrawScrollArrows(lppop, hdc);
1876 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1878 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1879 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1880 MENU_DrawScrollArrows(lppop, hdc);
1886 /***********************************************************************
1887 * MENU_SelectItem
1889 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1890 BOOL sendMenuSelect, HMENU topmenu )
1892 LPPOPUPMENU lppop;
1893 HDC hdc;
1895 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1897 lppop = MENU_GetMenu( hmenu );
1898 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1900 if (lppop->FocusedItem == wIndex) return;
1901 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1902 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1903 if (!top_popup) top_popup = lppop->hWnd;
1905 SelectObject( hdc, get_menu_font(FALSE));
1907 /* Clear previous highlighted item */
1908 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1910 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1911 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1912 lppop->Height, !(lppop->wFlags & MF_POPUP),
1913 ODA_SELECT );
1916 /* Highlight new item (if any) */
1917 lppop->FocusedItem = wIndex;
1918 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1920 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1921 lppop->items[wIndex].fState |= MF_HILITE;
1922 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1923 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1924 &lppop->items[wIndex], lppop->Height,
1925 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1927 if (sendMenuSelect)
1929 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1930 SendMessageW( hwndOwner, WM_MENUSELECT,
1931 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1932 ip->fType | ip->fState |
1933 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1936 else if (sendMenuSelect) {
1937 if(topmenu){
1938 int pos;
1939 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1940 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1941 MENUITEM *ip = &ptm->items[pos];
1942 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1943 ip->fType | ip->fState |
1944 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1948 ReleaseDC( lppop->hWnd, hdc );
1952 /***********************************************************************
1953 * MENU_MoveSelection
1955 * Moves currently selected item according to the offset parameter.
1956 * If there is no selection then it should select the last item if
1957 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1959 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1961 INT i;
1962 POPUPMENU *menu;
1964 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1966 menu = MENU_GetMenu( hmenu );
1967 if ((!menu) || (!menu->items)) return;
1969 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1971 if( menu->nItems == 1 ) return; else
1972 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1973 ; i += offset)
1974 if (!(menu->items[i].fType & MF_SEPARATOR))
1976 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1977 return;
1981 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1982 i >= 0 && i < menu->nItems ; i += offset)
1983 if (!(menu->items[i].fType & MF_SEPARATOR))
1985 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1986 return;
1991 /**********************************************************************
1992 * MENU_SetItemData
1994 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1995 * ModifyMenu().
1997 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1998 LPCWSTR str )
2000 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2001 TRACE("flags=%x str=%p\n", flags, str);
2003 if (IS_STRING_ITEM(flags))
2005 LPWSTR prevText = item->text;
2006 if (!str)
2008 flags |= MF_SEPARATOR;
2009 item->text = NULL;
2011 else
2013 LPWSTR text;
2014 /* Item beginning with a backspace is a help item */
2015 if (*str == '\b')
2017 flags |= MF_HELP;
2018 str++;
2020 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2021 return FALSE;
2022 strcpyW( text, str );
2023 item->text = text;
2025 item->hbmpItem = NULL;
2026 HeapFree( GetProcessHeap(), 0, prevText );
2028 else if(( flags & MFT_BITMAP)) {
2029 item->hbmpItem = HBITMAP_32(LOWORD(str));
2030 /* setting bitmap clears text */
2031 HeapFree( GetProcessHeap(), 0, item->text );
2032 item->text = NULL;
2035 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2037 if (flags & MF_OWNERDRAW)
2038 item->dwItemData = (DWORD_PTR)str;
2039 else
2040 item->dwItemData = 0;
2042 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2043 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2045 if (flags & MF_POPUP)
2047 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2048 if (menu) menu->wFlags |= MF_POPUP;
2049 else
2051 item->wID = 0;
2052 item->hSubMenu = 0;
2053 item->fType = 0;
2054 item->fState = 0;
2055 return FALSE;
2059 item->wID = id;
2060 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2062 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2063 flags |= MF_POPUP; /* keep popup */
2065 item->fType = flags & TYPE_MASK;
2066 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2067 for ModifyMenu, but Windows accepts it */
2068 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2070 /* Don't call SetRectEmpty here! */
2072 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2073 return TRUE;
2077 /**********************************************************************
2078 * MENU_InsertItem
2080 * Insert (allocate) a new item into a menu.
2082 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2084 MENUITEM *newItems;
2085 POPUPMENU *menu;
2087 if (!(menu = MENU_GetMenu(hMenu)))
2088 return NULL;
2090 /* Find where to insert new item */
2092 if (flags & MF_BYPOSITION) {
2093 if (pos > menu->nItems)
2094 pos = menu->nItems;
2095 } else {
2096 if (!MENU_FindItem( &hMenu, &pos, flags ))
2097 pos = menu->nItems;
2098 else {
2099 if (!(menu = MENU_GetMenu( hMenu )))
2100 return NULL;
2104 /* Create new items array */
2106 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2107 if (!newItems)
2109 WARN("allocation failed\n" );
2110 return NULL;
2112 if (menu->nItems > 0)
2114 /* Copy the old array into the new one */
2115 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2116 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2117 (menu->nItems-pos)*sizeof(MENUITEM) );
2118 HeapFree( GetProcessHeap(), 0, menu->items );
2120 menu->items = newItems;
2121 menu->nItems++;
2122 memset( &newItems[pos], 0, sizeof(*newItems) );
2123 menu->Height = 0; /* force size recalculate */
2124 return &newItems[pos];
2128 /**********************************************************************
2129 * MENU_ParseResource
2131 * Parse a standard menu resource and add items to the menu.
2132 * Return a pointer to the end of the resource.
2134 * NOTE: flags is equivalent to the mtOption field
2136 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2138 WORD flags, id = 0;
2139 LPCSTR str;
2140 BOOL end_flag;
2144 flags = GET_WORD(res);
2145 end_flag = flags & MF_END;
2146 /* Remove MF_END because it has the same value as MF_HILITE */
2147 flags &= ~MF_END;
2148 res += sizeof(WORD);
2149 if (!(flags & MF_POPUP))
2151 id = GET_WORD(res);
2152 res += sizeof(WORD);
2154 str = res;
2155 if (!unicode) res += strlen(str) + 1;
2156 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2157 if (flags & MF_POPUP)
2159 HMENU hSubMenu = CreatePopupMenu();
2160 if (!hSubMenu) return NULL;
2161 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2162 return NULL;
2163 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2164 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2166 else /* Not a popup */
2168 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2169 else AppendMenuW( hMenu, flags, id,
2170 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2172 } while (!end_flag);
2173 return res;
2177 /**********************************************************************
2178 * MENUEX_ParseResource
2180 * Parse an extended menu resource and add items to the menu.
2181 * Return a pointer to the end of the resource.
2183 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2185 WORD resinfo;
2186 do {
2187 MENUITEMINFOW mii;
2189 mii.cbSize = sizeof(mii);
2190 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2191 mii.fType = GET_DWORD(res);
2192 res += sizeof(DWORD);
2193 mii.fState = GET_DWORD(res);
2194 res += sizeof(DWORD);
2195 mii.wID = GET_DWORD(res);
2196 res += sizeof(DWORD);
2197 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2198 res += sizeof(WORD);
2199 /* Align the text on a word boundary. */
2200 res += (~((UINT_PTR)res - 1)) & 1;
2201 mii.dwTypeData = (LPWSTR) res;
2202 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2203 /* Align the following fields on a dword boundary. */
2204 res += (~((UINT_PTR)res - 1)) & 3;
2206 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2207 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2209 if (resinfo & 1) { /* Pop-up? */
2210 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2211 res += sizeof(DWORD);
2212 mii.hSubMenu = CreatePopupMenu();
2213 if (!mii.hSubMenu)
2214 return NULL;
2215 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2216 DestroyMenu(mii.hSubMenu);
2217 return NULL;
2219 mii.fMask |= MIIM_SUBMENU;
2220 mii.fType |= MF_POPUP;
2222 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2224 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2225 mii.wID, mii.fType);
2226 mii.fType |= MF_SEPARATOR;
2228 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2229 } while (!(resinfo & MF_END));
2230 return res;
2234 /***********************************************************************
2235 * MENU_GetSubPopup
2237 * Return the handle of the selected sub-popup menu (if any).
2239 static HMENU MENU_GetSubPopup( HMENU hmenu )
2241 POPUPMENU *menu;
2242 MENUITEM *item;
2244 menu = MENU_GetMenu( hmenu );
2246 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2248 item = &menu->items[menu->FocusedItem];
2249 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2250 return item->hSubMenu;
2251 return 0;
2255 /***********************************************************************
2256 * MENU_HideSubPopups
2258 * Hide the sub-popup menus of this menu.
2260 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2261 BOOL sendMenuSelect )
2263 POPUPMENU *menu = MENU_GetMenu( hmenu );
2265 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2267 if (menu && top_popup)
2269 HMENU hsubmenu;
2270 POPUPMENU *submenu;
2271 MENUITEM *item;
2273 if (menu->FocusedItem != NO_SELECTED_ITEM)
2275 item = &menu->items[menu->FocusedItem];
2276 if (!(item->fType & MF_POPUP) ||
2277 !(item->fState & MF_MOUSESELECT)) return;
2278 item->fState &= ~MF_MOUSESELECT;
2279 hsubmenu = item->hSubMenu;
2280 } else return;
2282 submenu = MENU_GetMenu( hsubmenu );
2283 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2284 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2285 DestroyWindow( submenu->hWnd );
2286 submenu->hWnd = 0;
2291 /***********************************************************************
2292 * MENU_ShowSubPopup
2294 * Display the sub-menu of the selected item of this menu.
2295 * Return the handle of the submenu, or hmenu if no submenu to display.
2297 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2298 BOOL selectFirst, UINT wFlags )
2300 RECT rect;
2301 POPUPMENU *menu;
2302 MENUITEM *item;
2303 HDC hdc;
2305 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2307 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2309 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2311 item = &menu->items[menu->FocusedItem];
2312 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2313 return hmenu;
2315 /* message must be sent before using item,
2316 because nearly everything may be changed by the application ! */
2318 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2319 if (!(wFlags & TPM_NONOTIFY))
2320 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2321 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2323 item = &menu->items[menu->FocusedItem];
2324 rect = item->rect;
2326 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2327 if (!(item->fState & MF_HILITE))
2329 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2330 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2332 SelectObject( hdc, get_menu_font(FALSE));
2334 item->fState |= MF_HILITE;
2335 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2336 ReleaseDC( menu->hWnd, hdc );
2338 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2339 item->rect = rect;
2341 item->fState |= MF_MOUSESELECT;
2343 if (IS_SYSTEM_MENU(menu))
2345 MENU_InitSysMenuPopup(item->hSubMenu,
2346 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2347 GetClassLongW( menu->hWnd, GCL_STYLE));
2349 NC_GetSysPopupPos( menu->hWnd, &rect );
2350 rect.top = rect.bottom;
2351 rect.right = GetSystemMetrics(SM_CXSIZE);
2352 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2354 else
2356 GetWindowRect( menu->hWnd, &rect );
2357 if (menu->wFlags & MF_POPUP)
2359 RECT rc = item->rect;
2361 MENU_AdjustMenuItemRect(menu, &rc);
2363 /* The first item in the popup menu has to be at the
2364 same y position as the focused menu item */
2365 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2366 rect.top += rc.top - MENU_TOP_MARGIN;
2367 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2368 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2369 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2371 else
2373 rect.left += item->rect.left;
2374 rect.top += item->rect.bottom;
2375 rect.right = item->rect.right - item->rect.left;
2376 rect.bottom = item->rect.bottom - item->rect.top;
2380 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2381 rect.left, rect.top, rect.right, rect.bottom );
2382 if (selectFirst)
2383 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2384 return item->hSubMenu;
2389 /**********************************************************************
2390 * MENU_IsMenuActive
2392 HWND MENU_IsMenuActive(void)
2394 return top_popup;
2397 /***********************************************************************
2398 * MENU_PtMenu
2400 * Walks menu chain trying to find a menu pt maps to.
2402 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2404 POPUPMENU *menu = MENU_GetMenu( hMenu );
2405 UINT item = menu->FocusedItem;
2406 HMENU ret;
2408 /* try subpopup first (if any) */
2409 ret = (item != NO_SELECTED_ITEM &&
2410 (menu->items[item].fType & MF_POPUP) &&
2411 (menu->items[item].fState & MF_MOUSESELECT))
2412 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2414 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2416 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2417 if( menu->wFlags & MF_POPUP )
2419 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2421 else if (ht == HTSYSMENU)
2422 ret = get_win_sys_menu( menu->hWnd );
2423 else if (ht == HTMENU)
2424 ret = GetMenu( menu->hWnd );
2426 return ret;
2429 /***********************************************************************
2430 * MENU_ExecFocusedItem
2432 * Execute a menu item (for instance when user pressed Enter).
2433 * Return the wID of the executed item. Otherwise, -1 indicating
2434 * that no menu item was executed, -2 if a popup is shown;
2435 * Have to receive the flags for the TrackPopupMenu options to avoid
2436 * sending unwanted message.
2439 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2441 MENUITEM *item;
2442 POPUPMENU *menu = MENU_GetMenu( hMenu );
2444 TRACE("%p hmenu=%p\n", pmt, hMenu);
2446 if (!menu || !menu->nItems ||
2447 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2449 item = &menu->items[menu->FocusedItem];
2451 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2453 if (!(item->fType & MF_POPUP))
2455 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2457 /* If TPM_RETURNCMD is set you return the id, but
2458 do not send a message to the owner */
2459 if(!(wFlags & TPM_RETURNCMD))
2461 if( menu->wFlags & MF_SYSMENU )
2462 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2463 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2464 else
2466 if (menu->dwStyle & MNS_NOTIFYBYPOS)
2467 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2468 (LPARAM)hMenu);
2469 else
2470 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2473 return item->wID;
2476 else
2478 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2479 return -2;
2482 return -1;
2485 /***********************************************************************
2486 * MENU_SwitchTracking
2488 * Helper function for menu navigation routines.
2490 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2492 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2493 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2495 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2497 if( pmt->hTopMenu != hPtMenu &&
2498 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2500 /* both are top level menus (system and menu-bar) */
2501 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2502 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2503 pmt->hTopMenu = hPtMenu;
2505 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2506 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2510 /***********************************************************************
2511 * MENU_ButtonDown
2513 * Return TRUE if we can go on with menu tracking.
2515 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2517 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2519 if (hPtMenu)
2521 UINT id = 0;
2522 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2523 MENUITEM *item;
2525 if( IS_SYSTEM_MENU(ptmenu) )
2526 item = ptmenu->items;
2527 else
2528 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2530 if( item )
2532 if( ptmenu->FocusedItem != id )
2533 MENU_SwitchTracking( pmt, hPtMenu, id );
2535 /* If the popup menu is not already "popped" */
2536 if(!(item->fState & MF_MOUSESELECT ))
2538 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2541 return TRUE;
2543 /* Else the click was on the menu bar, finish the tracking */
2545 return FALSE;
2548 /***********************************************************************
2549 * MENU_ButtonUp
2551 * Return the value of MENU_ExecFocusedItem if
2552 * the selected item was not a popup. Else open the popup.
2553 * A -1 return value indicates that we go on with menu tracking.
2556 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2558 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2560 if (hPtMenu)
2562 UINT id = 0;
2563 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2564 MENUITEM *item;
2566 if( IS_SYSTEM_MENU(ptmenu) )
2567 item = ptmenu->items;
2568 else
2569 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2571 if( item && (ptmenu->FocusedItem == id ))
2573 debug_print_menuitem ("FocusedItem: ", item, "");
2575 if( !(item->fType & MF_POPUP) )
2577 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2578 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2579 return executedMenuId;
2582 /* If we are dealing with the top-level menu */
2583 /* and this is a click on an already "popped" item: */
2584 /* Stop the menu tracking and close the opened submenus */
2585 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2586 return 0;
2588 ptmenu->bTimeToHide = TRUE;
2590 return -1;
2594 /***********************************************************************
2595 * MENU_MouseMove
2597 * Return TRUE if we can go on with menu tracking.
2599 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2601 UINT id = NO_SELECTED_ITEM;
2602 POPUPMENU *ptmenu = NULL;
2604 if( hPtMenu )
2606 ptmenu = MENU_GetMenu( hPtMenu );
2607 if( IS_SYSTEM_MENU(ptmenu) )
2608 id = 0;
2609 else
2610 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2613 if( id == NO_SELECTED_ITEM )
2615 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2616 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2619 else if( ptmenu->FocusedItem != id )
2621 MENU_SwitchTracking( pmt, hPtMenu, id );
2622 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2624 return TRUE;
2628 /***********************************************************************
2629 * MENU_SetCapture
2631 static void MENU_SetCapture( HWND hwnd )
2633 HWND previous = 0;
2635 SERVER_START_REQ( set_capture_window )
2637 req->handle = hwnd;
2638 req->flags = CAPTURE_MENU;
2639 if (!wine_server_call_err( req ))
2641 previous = reply->previous;
2642 hwnd = reply->full_handle;
2645 SERVER_END_REQ;
2647 if (previous && previous != hwnd)
2648 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2652 /***********************************************************************
2653 * MENU_DoNextMenu
2655 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2657 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2659 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2661 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2662 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2664 MDINEXTMENU next_menu;
2665 HMENU hNewMenu;
2666 HWND hNewWnd;
2667 UINT id = 0;
2669 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2670 next_menu.hmenuNext = 0;
2671 next_menu.hwndNext = 0;
2672 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2674 TRACE("%p [%p] -> %p [%p]\n",
2675 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2677 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2679 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2680 hNewWnd = pmt->hOwnerWnd;
2681 if( IS_SYSTEM_MENU(menu) )
2683 /* switch to the menu bar */
2685 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2687 if( vk == VK_LEFT )
2689 menu = MENU_GetMenu( hNewMenu );
2690 id = menu->nItems - 1;
2693 else if (style & WS_SYSMENU )
2695 /* switch to the system menu */
2696 hNewMenu = get_win_sys_menu( hNewWnd );
2698 else return FALSE;
2700 else /* application returned a new menu to switch to */
2702 hNewMenu = next_menu.hmenuNext;
2703 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2705 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2707 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2709 if (style & WS_SYSMENU &&
2710 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2712 /* get the real system menu */
2713 hNewMenu = get_win_sys_menu(hNewWnd);
2715 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2717 /* FIXME: Not sure what to do here;
2718 * perhaps try to track hNewMenu as a popup? */
2720 TRACE(" -- got confused.\n");
2721 return FALSE;
2724 else return FALSE;
2727 if( hNewMenu != pmt->hTopMenu )
2729 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2730 FALSE, 0 );
2731 if( pmt->hCurrentMenu != pmt->hTopMenu )
2732 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2735 if( hNewWnd != pmt->hOwnerWnd )
2737 pmt->hOwnerWnd = hNewWnd;
2738 MENU_SetCapture( pmt->hOwnerWnd );
2741 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2742 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2744 return TRUE;
2746 return FALSE;
2749 /***********************************************************************
2750 * MENU_SuspendPopup
2752 * The idea is not to show the popup if the next input message is
2753 * going to hide it anyway.
2755 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2757 MSG msg;
2759 msg.hwnd = pmt->hOwnerWnd;
2761 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2762 pmt->trackFlags |= TF_SKIPREMOVE;
2764 switch( uMsg )
2766 case WM_KEYDOWN:
2767 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2768 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2770 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2771 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2772 if( msg.message == WM_KEYDOWN &&
2773 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2775 pmt->trackFlags |= TF_SUSPENDPOPUP;
2776 return TRUE;
2779 break;
2782 /* failures go through this */
2783 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2784 return FALSE;
2787 /***********************************************************************
2788 * MENU_KeyEscape
2790 * Handle a VK_ESCAPE key event in a menu.
2792 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2794 BOOL bEndMenu = TRUE;
2796 if (pmt->hCurrentMenu != pmt->hTopMenu)
2798 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2800 if (menu->wFlags & MF_POPUP)
2802 HMENU hmenutmp, hmenuprev;
2804 hmenuprev = hmenutmp = pmt->hTopMenu;
2806 /* close topmost popup */
2807 while (hmenutmp != pmt->hCurrentMenu)
2809 hmenuprev = hmenutmp;
2810 hmenutmp = MENU_GetSubPopup( hmenuprev );
2813 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2814 pmt->hCurrentMenu = hmenuprev;
2815 bEndMenu = FALSE;
2819 return bEndMenu;
2822 /***********************************************************************
2823 * MENU_KeyLeft
2825 * Handle a VK_LEFT key event in a menu.
2827 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2829 POPUPMENU *menu;
2830 HMENU hmenutmp, hmenuprev;
2831 UINT prevcol;
2833 hmenuprev = hmenutmp = pmt->hTopMenu;
2834 menu = MENU_GetMenu( hmenutmp );
2836 /* Try to move 1 column left (if possible) */
2837 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2838 NO_SELECTED_ITEM ) {
2840 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2841 prevcol, TRUE, 0 );
2842 return;
2845 /* close topmost popup */
2846 while (hmenutmp != pmt->hCurrentMenu)
2848 hmenuprev = hmenutmp;
2849 hmenutmp = MENU_GetSubPopup( hmenuprev );
2852 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2853 pmt->hCurrentMenu = hmenuprev;
2855 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2857 /* move menu bar selection if no more popups are left */
2859 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2860 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2862 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2864 /* A sublevel menu was displayed - display the next one
2865 * unless there is another displacement coming up */
2867 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2868 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2869 pmt->hTopMenu, TRUE, wFlags);
2875 /***********************************************************************
2876 * MENU_KeyRight
2878 * Handle a VK_RIGHT key event in a menu.
2880 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2882 HMENU hmenutmp;
2883 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2884 UINT nextcol;
2886 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2887 pmt->hCurrentMenu,
2888 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2889 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2891 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2893 /* If already displaying a popup, try to display sub-popup */
2895 hmenutmp = pmt->hCurrentMenu;
2896 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2898 /* if subpopup was displayed then we are done */
2899 if (hmenutmp != pmt->hCurrentMenu) return;
2902 /* Check to see if there's another column */
2903 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2904 NO_SELECTED_ITEM ) {
2905 TRACE("Going to %d.\n", nextcol );
2906 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2907 nextcol, TRUE, 0 );
2908 return;
2911 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2913 if( pmt->hCurrentMenu != pmt->hTopMenu )
2915 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2916 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2917 } else hmenutmp = 0;
2919 /* try to move to the next item */
2920 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2921 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2923 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2924 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2925 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2926 pmt->hTopMenu, TRUE, wFlags);
2930 /***********************************************************************
2931 * MENU_TrackMenu
2933 * Menu tracking code.
2935 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2936 HWND hwnd, const RECT *lprect )
2938 MSG msg;
2939 POPUPMENU *menu;
2940 BOOL fRemove;
2941 INT executedMenuId = -1;
2942 MTRACKER mt;
2943 BOOL enterIdleSent = FALSE;
2945 mt.trackFlags = 0;
2946 mt.hCurrentMenu = hmenu;
2947 mt.hTopMenu = hmenu;
2948 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2949 mt.pt.x = x;
2950 mt.pt.y = y;
2952 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2953 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2955 fEndMenu = FALSE;
2956 if (!(menu = MENU_GetMenu( hmenu )))
2958 WARN("Invalid menu handle %p\n", hmenu);
2959 SetLastError(ERROR_INVALID_MENU_HANDLE);
2960 return FALSE;
2963 if (wFlags & TPM_BUTTONDOWN)
2965 /* Get the result in order to start the tracking or not */
2966 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2967 fEndMenu = !fRemove;
2970 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2972 MENU_SetCapture( mt.hOwnerWnd );
2974 while (!fEndMenu)
2976 menu = MENU_GetMenu( mt.hCurrentMenu );
2977 if (!menu) /* sometimes happens if I do a window manager close */
2978 break;
2980 /* we have to keep the message in the queue until it's
2981 * clear that menu loop is not over yet. */
2983 for (;;)
2985 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2987 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2988 /* remove the message from the queue */
2989 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2991 else
2993 if (!enterIdleSent)
2995 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2996 enterIdleSent = TRUE;
2997 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2999 WaitMessage();
3003 /* check if EndMenu() tried to cancel us, by posting this message */
3004 if(msg.message == WM_CANCELMODE)
3006 /* we are now out of the loop */
3007 fEndMenu = TRUE;
3009 /* remove the message from the queue */
3010 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3012 /* break out of internal loop, ala ESCAPE */
3013 break;
3016 TranslateMessage( &msg );
3017 mt.pt = msg.pt;
3019 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3020 enterIdleSent=FALSE;
3022 fRemove = FALSE;
3023 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3026 * Use the mouse coordinates in lParam instead of those in the MSG
3027 * struct to properly handle synthetic messages. They are already
3028 * in screen coordinates.
3030 mt.pt.x = (short)LOWORD(msg.lParam);
3031 mt.pt.y = (short)HIWORD(msg.lParam);
3033 /* Find a menu for this mouse event */
3034 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3036 switch(msg.message)
3038 /* no WM_NC... messages in captured state */
3040 case WM_RBUTTONDBLCLK:
3041 case WM_RBUTTONDOWN:
3042 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3043 /* fall through */
3044 case WM_LBUTTONDBLCLK:
3045 case WM_LBUTTONDOWN:
3046 /* If the message belongs to the menu, removes it from the queue */
3047 /* Else, end menu tracking */
3048 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3049 fEndMenu = !fRemove;
3050 break;
3052 case WM_RBUTTONUP:
3053 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3054 /* fall through */
3055 case WM_LBUTTONUP:
3056 /* Check if a menu was selected by the mouse */
3057 if (hmenu)
3059 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3060 TRACE("executedMenuId %d\n", executedMenuId);
3062 /* End the loop if executedMenuId is an item ID */
3063 /* or if the job was done (executedMenuId = 0). */
3064 fEndMenu = fRemove = (executedMenuId != -1);
3066 /* No menu was selected by the mouse */
3067 /* if the function was called by TrackPopupMenu, continue
3068 with the menu tracking. If not, stop it */
3069 else
3070 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3072 break;
3074 case WM_MOUSEMOVE:
3075 /* the selected menu item must be changed every time */
3076 /* the mouse moves. */
3078 if (hmenu)
3079 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3081 } /* switch(msg.message) - mouse */
3083 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3085 fRemove = TRUE; /* Keyboard messages are always removed */
3086 switch(msg.message)
3088 case WM_KEYDOWN:
3089 case WM_SYSKEYDOWN:
3090 switch(msg.wParam)
3092 case VK_MENU:
3093 fEndMenu = TRUE;
3094 break;
3096 case VK_HOME:
3097 case VK_END:
3098 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3099 NO_SELECTED_ITEM, FALSE, 0 );
3100 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3101 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3102 break;
3104 case VK_UP:
3105 case VK_DOWN: /* If on menu bar, pull-down the menu */
3107 menu = MENU_GetMenu( mt.hCurrentMenu );
3108 if (!(menu->wFlags & MF_POPUP))
3109 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3110 else /* otherwise try to move selection */
3111 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3112 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3113 break;
3115 case VK_LEFT:
3116 MENU_KeyLeft( &mt, wFlags );
3117 break;
3119 case VK_RIGHT:
3120 MENU_KeyRight( &mt, wFlags );
3121 break;
3123 case VK_ESCAPE:
3124 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3125 break;
3127 case VK_F1:
3129 HELPINFO hi;
3130 hi.cbSize = sizeof(HELPINFO);
3131 hi.iContextType = HELPINFO_MENUITEM;
3132 if (menu->FocusedItem == NO_SELECTED_ITEM)
3133 hi.iCtrlId = 0;
3134 else
3135 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3136 hi.hItemHandle = hmenu;
3137 hi.dwContextId = menu->dwContextHelpID;
3138 hi.MousePos = msg.pt;
3139 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3140 break;
3143 default:
3144 break;
3146 break; /* WM_KEYDOWN */
3148 case WM_CHAR:
3149 case WM_SYSCHAR:
3151 UINT pos;
3153 if (msg.wParam == '\r' || msg.wParam == ' ')
3155 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3156 fEndMenu = (executedMenuId != -2);
3158 break;
3161 /* Hack to avoid control chars. */
3162 /* We will find a better way real soon... */
3163 if (msg.wParam < 32) break;
3165 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3166 LOWORD(msg.wParam), FALSE );
3167 if (pos == (UINT)-2) fEndMenu = TRUE;
3168 else if (pos == (UINT)-1) MessageBeep(0);
3169 else
3171 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3172 TRUE, 0 );
3173 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3174 fEndMenu = (executedMenuId != -2);
3177 break;
3178 } /* switch(msg.message) - kbd */
3180 else
3182 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3183 DispatchMessageW( &msg );
3184 continue;
3187 if (!fEndMenu) fRemove = TRUE;
3189 /* finally remove message from the queue */
3191 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3192 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3193 else mt.trackFlags &= ~TF_SKIPREMOVE;
3196 MENU_SetCapture(0); /* release the capture */
3198 /* If dropdown is still painted and the close box is clicked on
3199 then the menu will be destroyed as part of the DispatchMessage above.
3200 This will then invalidate the menu handle in mt.hTopMenu. We should
3201 check for this first. */
3202 if( IsMenu( mt.hTopMenu ) )
3204 menu = MENU_GetMenu( mt.hTopMenu );
3206 if( IsWindow( mt.hOwnerWnd ) )
3208 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3210 if (menu && (menu->wFlags & MF_POPUP))
3212 DestroyWindow( menu->hWnd );
3213 menu->hWnd = 0;
3215 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3216 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3219 /* Reset the variable for hiding menu */
3220 if( menu ) menu->bTimeToHide = FALSE;
3223 /* The return value is only used by TrackPopupMenu */
3224 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3225 if (executedMenuId == -1) executedMenuId = 0;
3226 return executedMenuId;
3229 /***********************************************************************
3230 * MENU_InitTracking
3232 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3234 POPUPMENU *menu;
3236 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3238 HideCaret(0);
3240 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3241 if (!(wFlags & TPM_NONOTIFY))
3242 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3244 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3246 if (!(wFlags & TPM_NONOTIFY))
3248 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3249 /* If an app changed/recreated menu bar entries in WM_INITMENU
3250 * menu sizes will be recalculated once the menu created/shown.
3254 /* This makes the menus of applications built with Delphi work.
3255 * It also enables menus to be displayed in more than one window,
3256 * but there are some bugs left that need to be fixed in this case.
3258 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3260 return TRUE;
3262 /***********************************************************************
3263 * MENU_ExitTracking
3265 static BOOL MENU_ExitTracking(HWND hWnd)
3267 TRACE("hwnd=%p\n", hWnd);
3269 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3270 ShowCaret(0);
3271 top_popup = 0;
3272 return TRUE;
3275 /***********************************************************************
3276 * MENU_TrackMouseMenuBar
3278 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3280 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3282 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3283 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3285 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3287 if (IsMenu(hMenu))
3289 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3290 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3291 MENU_ExitTracking(hWnd);
3296 /***********************************************************************
3297 * MENU_TrackKbdMenuBar
3299 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3301 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3303 UINT uItem = NO_SELECTED_ITEM;
3304 HMENU hTrackMenu;
3305 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3307 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3309 /* find window that has a menu */
3311 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3312 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3314 /* check if we have to track a system menu */
3316 hTrackMenu = GetMenu( hwnd );
3317 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3319 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3320 hTrackMenu = get_win_sys_menu( hwnd );
3321 uItem = 0;
3322 wParam |= HTSYSMENU; /* prevent item lookup */
3325 if (!IsMenu( hTrackMenu )) return;
3327 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3329 if( wChar && wChar != ' ' )
3331 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3332 if ( uItem >= (UINT)(-2) )
3334 if( uItem == (UINT)(-1) ) MessageBeep(0);
3335 /* schedule end of menu tracking */
3336 wFlags |= TF_ENDMENU;
3337 goto track_menu;
3341 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3343 if (wParam & HTSYSMENU)
3345 /* prevent sysmenu activation for managed windows on Alt down/up */
3346 if (GetPropA( hwnd, "__wine_x11_managed" ))
3347 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3349 else
3351 if( uItem == NO_SELECTED_ITEM )
3352 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3353 else
3354 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3357 track_menu:
3358 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3359 MENU_ExitTracking( hwnd );
3363 /**********************************************************************
3364 * TrackPopupMenu (USER32.@)
3366 * Like the win32 API, the function return the command ID only if the
3367 * flag TPM_RETURNCMD is on.
3370 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3371 INT nReserved, HWND hWnd, const RECT *lpRect )
3373 BOOL ret = FALSE;
3375 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3376 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3378 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3380 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3381 if (!(wFlags & TPM_NONOTIFY))
3382 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3384 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3385 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3386 MENU_ExitTracking(hWnd);
3388 return ret;
3391 /**********************************************************************
3392 * TrackPopupMenuEx (USER32.@)
3394 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3395 HWND hWnd, LPTPMPARAMS lpTpm )
3397 FIXME("not fully implemented\n" );
3398 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3399 lpTpm ? &lpTpm->rcExclude : NULL );
3402 /***********************************************************************
3403 * PopupMenuWndProc
3405 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3407 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3409 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3411 switch(message)
3413 case WM_CREATE:
3415 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3416 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3417 return 0;
3420 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3421 return MA_NOACTIVATE;
3423 case WM_PAINT:
3425 PAINTSTRUCT ps;
3426 BeginPaint( hwnd, &ps );
3427 MENU_DrawPopupMenu( hwnd, ps.hdc,
3428 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3429 EndPaint( hwnd, &ps );
3430 return 0;
3432 case WM_ERASEBKGND:
3433 return 1;
3435 case WM_DESTROY:
3436 /* zero out global pointer in case resident popup window was destroyed. */
3437 if (hwnd == top_popup) top_popup = 0;
3438 break;
3440 case WM_SHOWWINDOW:
3442 if( wParam )
3444 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3446 else
3447 SetWindowLongPtrW( hwnd, 0, 0 );
3448 break;
3450 case MM_SETMENUHANDLE:
3451 SetWindowLongPtrW( hwnd, 0, wParam );
3452 break;
3454 case MM_GETMENUHANDLE:
3455 return GetWindowLongPtrW( hwnd, 0 );
3457 default:
3458 return DefWindowProcW( hwnd, message, wParam, lParam );
3460 return 0;
3464 /***********************************************************************
3465 * MENU_GetMenuBarHeight
3467 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3469 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3470 INT orgX, INT orgY )
3472 HDC hdc;
3473 RECT rectBar;
3474 LPPOPUPMENU lppop;
3476 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3478 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3480 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3481 SelectObject( hdc, get_menu_font(FALSE));
3482 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3483 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3484 ReleaseDC( hwnd, hdc );
3485 return lppop->Height;
3489 /*******************************************************************
3490 * ChangeMenuA (USER32.@)
3492 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3493 UINT id, UINT flags )
3495 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3496 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3497 id, data );
3498 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3499 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3500 id, data );
3501 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3502 flags & MF_BYPOSITION ? pos : id,
3503 flags & ~MF_REMOVE );
3504 /* Default: MF_INSERT */
3505 return InsertMenuA( hMenu, pos, flags, id, data );
3509 /*******************************************************************
3510 * ChangeMenuW (USER32.@)
3512 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3513 UINT id, UINT flags )
3515 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3516 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3517 id, data );
3518 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3519 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3520 id, data );
3521 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3522 flags & MF_BYPOSITION ? pos : id,
3523 flags & ~MF_REMOVE );
3524 /* Default: MF_INSERT */
3525 return InsertMenuW( hMenu, pos, flags, id, data );
3529 /*******************************************************************
3530 * CheckMenuItem (USER32.@)
3532 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3534 MENUITEM *item;
3535 DWORD ret;
3537 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3538 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3539 ret = item->fState & MF_CHECKED;
3540 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3541 else item->fState &= ~MF_CHECKED;
3542 return ret;
3546 /**********************************************************************
3547 * EnableMenuItem (USER32.@)
3549 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3551 UINT oldflags;
3552 MENUITEM *item;
3553 POPUPMENU *menu;
3555 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3557 /* Get the Popupmenu to access the owner menu */
3558 if (!(menu = MENU_GetMenu(hMenu)))
3559 return (UINT)-1;
3561 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3562 return (UINT)-1;
3564 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3565 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3567 /* If the close item in the system menu change update the close button */
3568 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3570 if (menu->hSysMenuOwner != 0)
3572 RECT rc;
3573 POPUPMENU* parentMenu;
3575 /* Get the parent menu to access*/
3576 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3577 return (UINT)-1;
3579 /* Refresh the frame to reflect the change */
3580 GetWindowRect(parentMenu->hWnd, &rc);
3581 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3582 rc.bottom = 0;
3583 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3587 return oldflags;
3591 /*******************************************************************
3592 * GetMenuStringA (USER32.@)
3594 INT WINAPI GetMenuStringA(
3595 HMENU hMenu, /* [in] menuhandle */
3596 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3597 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3598 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3599 UINT wFlags /* [in] MF_ flags */
3601 MENUITEM *item;
3603 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3604 if (str && nMaxSiz) str[0] = '\0';
3605 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3606 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3607 return 0;
3609 if (!item->text) return 0;
3610 if (!str || !nMaxSiz) return strlenW(item->text);
3611 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3612 str[nMaxSiz-1] = 0;
3613 TRACE("returning '%s'\n", str );
3614 return strlen(str);
3618 /*******************************************************************
3619 * GetMenuStringW (USER32.@)
3621 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3622 LPWSTR str, INT nMaxSiz, UINT wFlags )
3624 MENUITEM *item;
3626 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3627 if (str && nMaxSiz) str[0] = '\0';
3628 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3629 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3630 return 0;
3632 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3633 if( !(item->text)) {
3634 str[0] = 0;
3635 return 0;
3637 lstrcpynW( str, item->text, nMaxSiz );
3638 return strlenW(str);
3642 /**********************************************************************
3643 * HiliteMenuItem (USER32.@)
3645 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3646 UINT wHilite )
3648 LPPOPUPMENU menu;
3649 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3650 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3651 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3652 if (menu->FocusedItem == wItemID) return TRUE;
3653 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3654 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3655 return TRUE;
3659 /**********************************************************************
3660 * GetMenuState (USER32.@)
3662 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3664 MENUITEM *item;
3665 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3666 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3667 debug_print_menuitem (" item: ", item, "");
3668 if (item->fType & MF_POPUP)
3670 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3671 if (!menu) return -1;
3672 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3674 else
3676 /* We used to (from way back then) mask the result to 0xff. */
3677 /* I don't know why and it seems wrong as the documented */
3678 /* return flag MF_SEPARATOR is outside that mask. */
3679 return (item->fType | item->fState);
3684 /**********************************************************************
3685 * GetMenuItemCount (USER32.@)
3687 INT WINAPI GetMenuItemCount( HMENU hMenu )
3689 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3690 if (!menu) return -1;
3691 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3692 return menu->nItems;
3696 /**********************************************************************
3697 * GetMenuItemID (USER32.@)
3699 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3701 MENUITEM * lpmi;
3703 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3704 if (lpmi->fType & MF_POPUP) return -1;
3705 return lpmi->wID;
3710 /*******************************************************************
3711 * InsertMenuW (USER32.@)
3713 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3714 UINT_PTR id, LPCWSTR str )
3716 MENUITEM *item;
3718 if (IS_STRING_ITEM(flags) && str)
3719 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3720 hMenu, pos, flags, id, debugstr_w(str) );
3721 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3722 hMenu, pos, flags, id, str );
3724 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3726 if (!(MENU_SetItemData( item, flags, id, str )))
3728 RemoveMenu( hMenu, pos, flags );
3729 return FALSE;
3732 item->hCheckBit = item->hUnCheckBit = 0;
3733 return TRUE;
3737 /*******************************************************************
3738 * InsertMenuA (USER32.@)
3740 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3741 UINT_PTR id, LPCSTR str )
3743 BOOL ret = FALSE;
3745 if (IS_STRING_ITEM(flags) && str)
3747 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3748 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3749 if (newstr)
3751 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3752 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3753 HeapFree( GetProcessHeap(), 0, newstr );
3755 return ret;
3757 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3761 /*******************************************************************
3762 * AppendMenuA (USER32.@)
3764 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3765 UINT_PTR id, LPCSTR data )
3767 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3771 /*******************************************************************
3772 * AppendMenuW (USER32.@)
3774 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3775 UINT_PTR id, LPCWSTR data )
3777 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3781 /**********************************************************************
3782 * RemoveMenu (USER32.@)
3784 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3786 LPPOPUPMENU menu;
3787 MENUITEM *item;
3789 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3790 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3791 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3793 /* Remove item */
3795 MENU_FreeItemData( item );
3797 if (--menu->nItems == 0)
3799 HeapFree( GetProcessHeap(), 0, menu->items );
3800 menu->items = NULL;
3802 else
3804 while(nPos < menu->nItems)
3806 *item = *(item+1);
3807 item++;
3808 nPos++;
3810 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3811 menu->nItems * sizeof(MENUITEM) );
3813 return TRUE;
3817 /**********************************************************************
3818 * DeleteMenu (USER32.@)
3820 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3822 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3823 if (!item) return FALSE;
3824 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3825 /* nPos is now the position of the item */
3826 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3827 return TRUE;
3831 /*******************************************************************
3832 * ModifyMenuW (USER32.@)
3834 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3835 UINT_PTR id, LPCWSTR str )
3837 MENUITEM *item;
3839 if (IS_STRING_ITEM(flags))
3840 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3841 else
3842 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3844 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3845 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3846 return MENU_SetItemData( item, flags, id, str );
3850 /*******************************************************************
3851 * ModifyMenuA (USER32.@)
3853 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3854 UINT_PTR id, LPCSTR str )
3856 BOOL ret = FALSE;
3858 if (IS_STRING_ITEM(flags) && str)
3860 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3861 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3862 if (newstr)
3864 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3865 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3866 HeapFree( GetProcessHeap(), 0, newstr );
3868 return ret;
3870 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3874 /**********************************************************************
3875 * CreatePopupMenu (USER32.@)
3877 HMENU WINAPI CreatePopupMenu(void)
3879 HMENU hmenu;
3880 POPUPMENU *menu;
3882 if (!(hmenu = CreateMenu())) return 0;
3883 menu = MENU_GetMenu( hmenu );
3884 menu->wFlags |= MF_POPUP;
3885 menu->bTimeToHide = FALSE;
3886 return hmenu;
3890 /**********************************************************************
3891 * GetMenuCheckMarkDimensions (USER.417)
3892 * GetMenuCheckMarkDimensions (USER32.@)
3894 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3896 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3900 /**********************************************************************
3901 * SetMenuItemBitmaps (USER32.@)
3903 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3904 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3906 MENUITEM *item;
3907 TRACE("(%p, %04x, %04x, %p, %p)\n",
3908 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3909 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3911 if (!hNewCheck && !hNewUnCheck)
3913 item->fState &= ~MF_USECHECKBITMAPS;
3915 else /* Install new bitmaps */
3917 item->hCheckBit = hNewCheck;
3918 item->hUnCheckBit = hNewUnCheck;
3919 item->fState |= MF_USECHECKBITMAPS;
3921 return TRUE;
3925 /**********************************************************************
3926 * CreateMenu (USER32.@)
3928 HMENU WINAPI CreateMenu(void)
3930 HMENU hMenu;
3931 LPPOPUPMENU menu;
3932 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3933 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3935 ZeroMemory(menu, sizeof(POPUPMENU));
3936 menu->wMagic = MENU_MAGIC;
3937 menu->FocusedItem = NO_SELECTED_ITEM;
3938 menu->bTimeToHide = FALSE;
3940 TRACE("return %p\n", hMenu );
3942 return hMenu;
3946 /**********************************************************************
3947 * DestroyMenu (USER32.@)
3949 BOOL WINAPI DestroyMenu( HMENU hMenu )
3951 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3953 TRACE("(%p)\n", hMenu);
3956 if (!lppop) return FALSE;
3958 lppop->wMagic = 0; /* Mark it as destroyed */
3960 /* DestroyMenu should not destroy system menu popup owner */
3961 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3963 DestroyWindow( lppop->hWnd );
3964 lppop->hWnd = 0;
3967 if (lppop->items) /* recursively destroy submenus */
3969 int i;
3970 MENUITEM *item = lppop->items;
3971 for (i = lppop->nItems; i > 0; i--, item++)
3973 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3974 MENU_FreeItemData( item );
3976 HeapFree( GetProcessHeap(), 0, lppop->items );
3978 USER_HEAP_FREE( hMenu );
3979 return TRUE;
3983 /**********************************************************************
3984 * GetSystemMenu (USER32.@)
3986 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3988 WND *wndPtr = WIN_GetPtr( hWnd );
3989 HMENU retvalue = 0;
3991 if (wndPtr == WND_DESKTOP) return 0;
3992 if (wndPtr == WND_OTHER_PROCESS)
3994 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3996 else if (wndPtr)
3998 if (wndPtr->hSysMenu && bRevert)
4000 DestroyMenu(wndPtr->hSysMenu);
4001 wndPtr->hSysMenu = 0;
4004 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4005 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4007 if( wndPtr->hSysMenu )
4009 POPUPMENU *menu;
4010 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4012 /* Store the dummy sysmenu handle to facilitate the refresh */
4013 /* of the close button if the SC_CLOSE item change */
4014 menu = MENU_GetMenu(retvalue);
4015 if ( menu )
4016 menu->hSysMenuOwner = wndPtr->hSysMenu;
4018 WIN_ReleasePtr( wndPtr );
4020 return bRevert ? 0 : retvalue;
4024 /*******************************************************************
4025 * SetSystemMenu (USER32.@)
4027 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4029 WND *wndPtr = WIN_GetPtr( hwnd );
4031 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4033 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4034 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4035 WIN_ReleasePtr( wndPtr );
4036 return TRUE;
4038 return FALSE;
4042 /**********************************************************************
4043 * GetMenu (USER32.@)
4045 HMENU WINAPI GetMenu( HWND hWnd )
4047 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4048 TRACE("for %p returning %p\n", hWnd, retvalue);
4049 return retvalue;
4052 /**********************************************************************
4053 * GetMenuBarInfo (USER32.@)
4055 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4057 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4058 return FALSE;
4061 /**********************************************************************
4062 * MENU_SetMenu
4064 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4065 * SetWindowPos call that would result if SetMenu were called directly.
4067 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4069 TRACE("(%p, %p);\n", hWnd, hMenu);
4071 if (hMenu && !IsMenu(hMenu))
4073 WARN("hMenu %p is not a menu handle\n", hMenu);
4074 return FALSE;
4076 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4077 return FALSE;
4079 hWnd = WIN_GetFullHandle( hWnd );
4080 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4082 if (hMenu != 0)
4084 LPPOPUPMENU lpmenu;
4086 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4088 lpmenu->hWnd = hWnd;
4089 lpmenu->Height = 0; /* Make sure we recalculate the size */
4091 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4092 return TRUE;
4096 /**********************************************************************
4097 * SetMenu (USER32.@)
4099 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4101 if(!MENU_SetMenu(hWnd, hMenu))
4102 return FALSE;
4104 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4105 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4106 return TRUE;
4110 /**********************************************************************
4111 * GetSubMenu (USER32.@)
4113 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4115 MENUITEM * lpmi;
4117 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4118 if (!(lpmi->fType & MF_POPUP)) return 0;
4119 return lpmi->hSubMenu;
4123 /**********************************************************************
4124 * DrawMenuBar (USER32.@)
4126 BOOL WINAPI DrawMenuBar( HWND hWnd )
4128 LPPOPUPMENU lppop;
4129 HMENU hMenu = GetMenu(hWnd);
4131 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4132 return FALSE;
4133 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4135 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4136 lppop->hwndOwner = hWnd;
4137 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4138 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4139 return TRUE;
4142 /***********************************************************************
4143 * DrawMenuBarTemp (USER32.@)
4145 * UNDOCUMENTED !!
4147 * called by W98SE desk.cpl Control Panel Applet
4149 * Not 100% sure about the param names, but close.
4151 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4153 LPPOPUPMENU lppop;
4154 UINT i,retvalue;
4155 HFONT hfontOld = 0;
4156 BOOL flat_menu = FALSE;
4158 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4160 if (!hMenu)
4161 hMenu = GetMenu(hwnd);
4163 if (!hFont)
4164 hFont = get_menu_font(FALSE);
4166 lppop = MENU_GetMenu( hMenu );
4167 if (lppop == NULL || lprect == NULL)
4169 retvalue = GetSystemMetrics(SM_CYMENU);
4170 goto END;
4173 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4175 hfontOld = SelectObject( hDC, hFont);
4177 if (lppop->Height == 0)
4178 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4180 lprect->bottom = lprect->top + lppop->Height;
4182 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4184 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4185 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4186 LineTo( hDC, lprect->right, lprect->bottom );
4188 if (lppop->nItems == 0)
4190 retvalue = GetSystemMetrics(SM_CYMENU);
4191 goto END;
4194 for (i = 0; i < lppop->nItems; i++)
4196 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4197 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4199 retvalue = lppop->Height;
4201 END:
4202 if (hfontOld) SelectObject (hDC, hfontOld);
4203 return retvalue;
4206 /***********************************************************************
4207 * EndMenu (USER.187)
4208 * EndMenu (USER32.@)
4210 void WINAPI EndMenu(void)
4212 /* if we are in the menu code, and it is active */
4213 if (!fEndMenu && top_popup)
4215 /* terminate the menu handling code */
4216 fEndMenu = TRUE;
4218 /* needs to be posted to wakeup the internal menu handler */
4219 /* which will now terminate the menu, in the event that */
4220 /* the main window was minimized, or lost focus, so we */
4221 /* don't end up with an orphaned menu */
4222 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4227 /***********************************************************************
4228 * LookupMenuHandle (USER.217)
4230 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4232 HMENU hmenu32 = HMENU_32(hmenu);
4233 UINT id32 = id;
4234 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4235 else return HMENU_16(hmenu32);
4239 /**********************************************************************
4240 * LoadMenu (USER.150)
4242 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4244 HRSRC16 hRsrc;
4245 HGLOBAL16 handle;
4246 HMENU16 hMenu;
4248 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4249 if (!name) return 0;
4251 instance = GetExePtr( instance );
4252 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4253 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4254 hMenu = LoadMenuIndirect16(LockResource16(handle));
4255 FreeResource16( handle );
4256 return hMenu;
4260 /*****************************************************************
4261 * LoadMenuA (USER32.@)
4263 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4265 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4266 if (!hrsrc) return 0;
4267 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4271 /*****************************************************************
4272 * LoadMenuW (USER32.@)
4274 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4276 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4277 if (!hrsrc) return 0;
4278 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4282 /**********************************************************************
4283 * LoadMenuIndirect (USER.220)
4285 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4287 HMENU hMenu;
4288 WORD version, offset;
4289 LPCSTR p = (LPCSTR)template;
4291 TRACE("(%p)\n", template );
4292 version = GET_WORD(p);
4293 p += sizeof(WORD);
4294 if (version)
4296 WARN("version must be 0 for Win16\n" );
4297 return 0;
4299 offset = GET_WORD(p);
4300 p += sizeof(WORD) + offset;
4301 if (!(hMenu = CreateMenu())) return 0;
4302 if (!MENU_ParseResource( p, hMenu, FALSE ))
4304 DestroyMenu( hMenu );
4305 return 0;
4307 return HMENU_16(hMenu);
4311 /**********************************************************************
4312 * LoadMenuIndirectW (USER32.@)
4314 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4316 HMENU hMenu;
4317 WORD version, offset;
4318 LPCSTR p = (LPCSTR)template;
4320 version = GET_WORD(p);
4321 p += sizeof(WORD);
4322 TRACE("%p, ver %d\n", template, version );
4323 switch (version)
4325 case 0: /* standard format is version of 0 */
4326 offset = GET_WORD(p);
4327 p += sizeof(WORD) + offset;
4328 if (!(hMenu = CreateMenu())) return 0;
4329 if (!MENU_ParseResource( p, hMenu, TRUE ))
4331 DestroyMenu( hMenu );
4332 return 0;
4334 return hMenu;
4335 case 1: /* extended format is version of 1 */
4336 offset = GET_WORD(p);
4337 p += sizeof(WORD) + offset;
4338 if (!(hMenu = CreateMenu())) return 0;
4339 if (!MENUEX_ParseResource( p, hMenu))
4341 DestroyMenu( hMenu );
4342 return 0;
4344 return hMenu;
4345 default:
4346 ERR("version %d not supported.\n", version);
4347 return 0;
4352 /**********************************************************************
4353 * LoadMenuIndirectA (USER32.@)
4355 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4357 return LoadMenuIndirectW( template );
4361 /**********************************************************************
4362 * IsMenu (USER32.@)
4364 BOOL WINAPI IsMenu(HMENU hmenu)
4366 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4368 if (!menu)
4370 SetLastError(ERROR_INVALID_MENU_HANDLE);
4371 return FALSE;
4373 return TRUE;
4376 /**********************************************************************
4377 * GetMenuItemInfo_common
4380 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4381 LPMENUITEMINFOW lpmii, BOOL unicode)
4383 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4385 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4387 if (!menu)
4388 return FALSE;
4390 if( lpmii->fMask & MIIM_TYPE) {
4391 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4392 WARN("invalid combination of fMask bits used\n");
4393 /* this does not happen on Win9x/ME */
4394 SetLastError( ERROR_INVALID_PARAMETER);
4395 return FALSE;
4397 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4398 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4399 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4400 if( lpmii->fType & MFT_BITMAP) {
4401 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4402 lpmii->cch = 0;
4403 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4404 /* this does not happen on Win9x/ME */
4405 lpmii->dwTypeData = 0;
4406 lpmii->cch = 0;
4410 /* copy the text string */
4411 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4412 if( !menu->text ) {
4413 if(lpmii->dwTypeData && lpmii->cch) {
4414 lpmii->cch = 0;
4415 if( unicode)
4416 *((WCHAR *)lpmii->dwTypeData) = 0;
4417 else
4418 *((CHAR *)lpmii->dwTypeData) = 0;
4420 } else {
4421 int len;
4422 if (unicode)
4424 len = strlenW(menu->text);
4425 if(lpmii->dwTypeData && lpmii->cch)
4426 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4428 else
4430 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4431 0, NULL, NULL ) - 1;
4432 if(lpmii->dwTypeData && lpmii->cch)
4433 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4434 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4435 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4437 /* if we've copied a substring we return its length */
4438 if(lpmii->dwTypeData && lpmii->cch)
4439 if (lpmii->cch <= len + 1)
4440 lpmii->cch--;
4441 else
4442 lpmii->cch = len;
4443 else {
4444 /* return length of string */
4445 /* not on Win9x/ME if fType & MFT_BITMAP */
4446 lpmii->cch = len;
4451 if (lpmii->fMask & MIIM_FTYPE)
4452 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4454 if (lpmii->fMask & MIIM_BITMAP)
4455 lpmii->hbmpItem = menu->hbmpItem;
4457 if (lpmii->fMask & MIIM_STATE)
4458 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4460 if (lpmii->fMask & MIIM_ID)
4461 lpmii->wID = menu->wID;
4463 if (lpmii->fMask & MIIM_SUBMENU)
4464 lpmii->hSubMenu = menu->hSubMenu;
4465 else {
4466 /* hSubMenu is always cleared
4467 * (not on Win9x/ME ) */
4468 lpmii->hSubMenu = 0;
4471 if (lpmii->fMask & MIIM_CHECKMARKS) {
4472 lpmii->hbmpChecked = menu->hCheckBit;
4473 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4475 if (lpmii->fMask & MIIM_DATA)
4476 lpmii->dwItemData = menu->dwItemData;
4478 return TRUE;
4481 /**********************************************************************
4482 * GetMenuItemInfoA (USER32.@)
4484 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4485 LPMENUITEMINFOA lpmii)
4487 BOOL ret;
4488 MENUITEMINFOA mii;
4489 if( lpmii->cbSize != sizeof( mii) &&
4490 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4491 SetLastError( ERROR_INVALID_PARAMETER);
4492 return FALSE;
4494 memcpy( &mii, lpmii, lpmii->cbSize);
4495 mii.cbSize = sizeof( mii);
4496 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4497 (LPMENUITEMINFOW)&mii, FALSE);
4498 mii.cbSize = lpmii->cbSize;
4499 memcpy( lpmii, &mii, mii.cbSize);
4500 return ret;
4503 /**********************************************************************
4504 * GetMenuItemInfoW (USER32.@)
4506 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4507 LPMENUITEMINFOW lpmii)
4509 BOOL ret;
4510 MENUITEMINFOW mii;
4511 if( lpmii->cbSize != sizeof( mii) &&
4512 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4513 SetLastError( ERROR_INVALID_PARAMETER);
4514 return FALSE;
4516 memcpy( &mii, lpmii, lpmii->cbSize);
4517 mii.cbSize = sizeof( mii);
4518 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4519 mii.cbSize = lpmii->cbSize;
4520 memcpy( lpmii, &mii, mii.cbSize);
4521 return ret;
4525 /* set a menu item text from a ASCII or Unicode string */
4526 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4528 if (!text)
4529 menu->text = NULL;
4530 else if (unicode)
4532 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4533 strcpyW( menu->text, text );
4535 else
4537 LPCSTR str = (LPCSTR)text;
4538 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4539 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4540 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4545 /**********************************************************************
4546 * SetMenuItemInfo_common
4549 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4550 const MENUITEMINFOW *lpmii,
4551 BOOL unicode)
4553 if (!menu) return FALSE;
4555 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4557 if (lpmii->fMask & MIIM_TYPE ) {
4558 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4559 WARN("invalid combination of fMask bits used\n");
4560 /* this does not happen on Win9x/ME */
4561 SetLastError( ERROR_INVALID_PARAMETER);
4562 return FALSE;
4565 /* Remove the old type bits and replace them with the new ones */
4566 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4567 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4569 if (IS_STRING_ITEM(menu->fType)) {
4570 HeapFree(GetProcessHeap(), 0, menu->text);
4571 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4572 } else if( (menu->fType) & MFT_BITMAP)
4573 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4576 if (lpmii->fMask & MIIM_FTYPE ) {
4577 if(( lpmii->fType & MFT_BITMAP)) {
4578 SetLastError( ERROR_INVALID_PARAMETER);
4579 return FALSE;
4581 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4582 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4584 if (lpmii->fMask & MIIM_STRING ) {
4585 /* free the string when used */
4586 HeapFree(GetProcessHeap(), 0, menu->text);
4587 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4590 if (lpmii->fMask & MIIM_STATE)
4592 /* Other menu items having MFS_DEFAULT are not converted
4593 to normal items */
4594 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4597 if (lpmii->fMask & MIIM_ID)
4598 menu->wID = lpmii->wID;
4600 if (lpmii->fMask & MIIM_SUBMENU) {
4601 menu->hSubMenu = lpmii->hSubMenu;
4602 if (menu->hSubMenu) {
4603 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4604 if (subMenu) {
4605 subMenu->wFlags |= MF_POPUP;
4606 menu->fType |= MF_POPUP;
4608 else {
4609 SetLastError( ERROR_INVALID_PARAMETER);
4610 return FALSE;
4613 else
4614 menu->fType &= ~MF_POPUP;
4617 if (lpmii->fMask & MIIM_CHECKMARKS)
4619 menu->hCheckBit = lpmii->hbmpChecked;
4620 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4622 if (lpmii->fMask & MIIM_DATA)
4623 menu->dwItemData = lpmii->dwItemData;
4625 if (lpmii->fMask & MIIM_BITMAP)
4626 menu->hbmpItem = lpmii->hbmpItem;
4628 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4629 menu->fType |= MFT_SEPARATOR;
4631 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4632 return TRUE;
4635 /**********************************************************************
4636 * SetMenuItemInfoA (USER32.@)
4638 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4639 const MENUITEMINFOA *lpmii)
4641 MENUITEMINFOA mii;
4642 if( lpmii->cbSize != sizeof( mii) &&
4643 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4644 SetLastError( ERROR_INVALID_PARAMETER);
4645 return FALSE;
4647 memcpy( &mii, lpmii, lpmii->cbSize);
4648 if( lpmii->cbSize != sizeof( mii)) {
4649 mii.cbSize = sizeof( mii);
4650 mii.hbmpItem = NULL;
4652 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4653 (const MENUITEMINFOW *)&mii, FALSE);
4656 /**********************************************************************
4657 * SetMenuItemInfoW (USER32.@)
4659 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4660 const MENUITEMINFOW *lpmii)
4662 MENUITEMINFOW mii;
4663 if( lpmii->cbSize != sizeof( mii) &&
4664 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4665 SetLastError( ERROR_INVALID_PARAMETER);
4666 return FALSE;
4668 memcpy( &mii, lpmii, lpmii->cbSize);
4669 if( lpmii->cbSize != sizeof( mii)) {
4670 mii.cbSize = sizeof( mii);
4671 mii.hbmpItem = NULL;
4673 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4674 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4677 /**********************************************************************
4678 * SetMenuDefaultItem (USER32.@)
4681 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4683 UINT i;
4684 POPUPMENU *menu;
4685 MENUITEM *item;
4687 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4689 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4691 /* reset all default-item flags */
4692 item = menu->items;
4693 for (i = 0; i < menu->nItems; i++, item++)
4695 item->fState &= ~MFS_DEFAULT;
4698 /* no default item */
4699 if ( -1 == uItem)
4701 return TRUE;
4704 item = menu->items;
4705 if ( bypos )
4707 if ( uItem >= menu->nItems ) return FALSE;
4708 item[uItem].fState |= MFS_DEFAULT;
4709 return TRUE;
4711 else
4713 for (i = 0; i < menu->nItems; i++, item++)
4715 if (item->wID == uItem)
4717 item->fState |= MFS_DEFAULT;
4718 return TRUE;
4723 return FALSE;
4726 /**********************************************************************
4727 * GetMenuDefaultItem (USER32.@)
4729 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4731 POPUPMENU *menu;
4732 MENUITEM * item;
4733 UINT i = 0;
4735 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4737 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4739 /* find default item */
4740 item = menu->items;
4742 /* empty menu */
4743 if (! item) return -1;
4745 while ( !( item->fState & MFS_DEFAULT ) )
4747 i++; item++;
4748 if (i >= menu->nItems ) return -1;
4751 /* default: don't return disabled items */
4752 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4754 /* search rekursiv when needed */
4755 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4757 UINT ret;
4758 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4759 if ( -1 != ret ) return ret;
4761 /* when item not found in submenu, return the popup item */
4763 return ( bypos ) ? i : item->wID;
4768 /**********************************************************************
4769 * InsertMenuItemA (USER32.@)
4771 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4772 const MENUITEMINFOA *lpmii)
4774 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4775 MENUITEMINFOA mii;
4776 if( lpmii->cbSize != sizeof( mii) &&
4777 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4778 SetLastError( ERROR_INVALID_PARAMETER);
4779 return FALSE;
4781 memcpy( &mii, lpmii, lpmii->cbSize);
4782 if( lpmii->cbSize != sizeof( mii)) {
4783 mii.cbSize = sizeof( mii);
4784 mii.hbmpItem = NULL;
4786 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4790 /**********************************************************************
4791 * InsertMenuItemW (USER32.@)
4793 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4794 const MENUITEMINFOW *lpmii)
4796 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4797 MENUITEMINFOW mii;
4798 if( lpmii->cbSize != sizeof( mii) &&
4799 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4800 SetLastError( ERROR_INVALID_PARAMETER);
4801 return FALSE;
4803 memcpy( &mii, lpmii, lpmii->cbSize);
4804 if( lpmii->cbSize != sizeof( mii)) {
4805 mii.cbSize = sizeof( mii);
4806 mii.hbmpItem = NULL;
4808 return SetMenuItemInfo_common(item, &mii, TRUE);
4811 /**********************************************************************
4812 * CheckMenuRadioItem (USER32.@)
4815 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4816 UINT first, UINT last, UINT check,
4817 UINT bypos)
4819 BOOL done = FALSE;
4820 UINT i;
4821 MENUITEM *mi_first = NULL, *mi_check;
4822 HMENU m_first, m_check;
4824 TRACE("%p: %u-%u, check %u, flags %04x\n", hMenu, first, last, check, bypos);
4826 for (i = first; i <= last; i++)
4828 UINT pos = i;
4830 if (!mi_first)
4832 m_first = hMenu;
4833 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4834 if (!mi_first) continue;
4835 mi_check = mi_first;
4836 m_check = m_first;
4838 else
4840 m_check = hMenu;
4841 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4842 if (!mi_check) continue;
4845 if (m_first != m_check) continue;
4846 if (mi_check->fType == MFT_SEPARATOR) continue;
4848 if (i == check)
4850 mi_check->fType |= MFT_RADIOCHECK;
4851 mi_check->fState |= MFS_CHECKED;
4852 done = TRUE;
4854 else
4856 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4857 mi_check->fState &= ~MFS_CHECKED;
4861 return done;
4865 /**********************************************************************
4866 * GetMenuItemRect (USER32.@)
4868 * ATTENTION: Here, the returned values in rect are the screen
4869 * coordinates of the item just like if the menu was
4870 * always on the upper left side of the application.
4873 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4874 LPRECT rect)
4876 POPUPMENU *itemMenu;
4877 MENUITEM *item;
4878 HWND referenceHwnd;
4880 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4882 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4883 referenceHwnd = hwnd;
4885 if(!hwnd)
4887 itemMenu = MENU_GetMenu(hMenu);
4888 if (itemMenu == NULL)
4889 return FALSE;
4891 if(itemMenu->hWnd == 0)
4892 return FALSE;
4893 referenceHwnd = itemMenu->hWnd;
4896 if ((rect == NULL) || (item == NULL))
4897 return FALSE;
4899 *rect = item->rect;
4901 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4903 return TRUE;
4907 /**********************************************************************
4908 * SetMenuInfo (USER32.@)
4910 * FIXME
4911 * MIM_APPLYTOSUBMENUS
4912 * actually use the items to draw the menu
4914 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4916 POPUPMENU *menu;
4918 TRACE("(%p %p)\n", hMenu, lpmi);
4920 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4923 if (lpmi->fMask & MIM_BACKGROUND)
4924 menu->hbrBack = lpmi->hbrBack;
4926 if (lpmi->fMask & MIM_HELPID)
4927 menu->dwContextHelpID = lpmi->dwContextHelpID;
4929 if (lpmi->fMask & MIM_MAXHEIGHT)
4930 menu->cyMax = lpmi->cyMax;
4932 if (lpmi->fMask & MIM_MENUDATA)
4933 menu->dwMenuData = lpmi->dwMenuData;
4935 if (lpmi->fMask & MIM_STYLE)
4937 menu->dwStyle = lpmi->dwStyle;
4938 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4939 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4940 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4941 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
4944 return TRUE;
4946 return FALSE;
4949 /**********************************************************************
4950 * GetMenuInfo (USER32.@)
4952 * NOTES
4953 * win98/NT5.0
4956 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4957 { POPUPMENU *menu;
4959 TRACE("(%p %p)\n", hMenu, lpmi);
4961 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4964 if (lpmi->fMask & MIM_BACKGROUND)
4965 lpmi->hbrBack = menu->hbrBack;
4967 if (lpmi->fMask & MIM_HELPID)
4968 lpmi->dwContextHelpID = menu->dwContextHelpID;
4970 if (lpmi->fMask & MIM_MAXHEIGHT)
4971 lpmi->cyMax = menu->cyMax;
4973 if (lpmi->fMask & MIM_MENUDATA)
4974 lpmi->dwMenuData = menu->dwMenuData;
4976 if (lpmi->fMask & MIM_STYLE)
4977 lpmi->dwStyle = menu->dwStyle;
4979 return TRUE;
4981 return FALSE;
4985 /**********************************************************************
4986 * SetMenuContextHelpId (USER32.@)
4988 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4990 LPPOPUPMENU menu;
4992 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
4994 if ((menu = MENU_GetMenu(hMenu)))
4996 menu->dwContextHelpID = dwContextHelpID;
4997 return TRUE;
4999 return FALSE;
5003 /**********************************************************************
5004 * GetMenuContextHelpId (USER32.@)
5006 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5008 LPPOPUPMENU menu;
5010 TRACE("(%p)\n", hMenu);
5012 if ((menu = MENU_GetMenu(hMenu)))
5014 return menu->dwContextHelpID;
5016 return 0;
5019 /**********************************************************************
5020 * MenuItemFromPoint (USER32.@)
5022 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5024 POPUPMENU *menu = MENU_GetMenu(hMenu);
5025 UINT pos;
5027 /*FIXME: Do we have to handle hWnd here? */
5028 if (!menu) return -1;
5029 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5030 return pos;
5034 /**********************************************************************
5035 * translate_accelerator
5037 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5038 BYTE fVirt, WORD key, WORD cmd )
5040 INT mask = 0;
5041 UINT mesg = 0;
5043 if (wParam != key) return FALSE;
5045 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5046 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5047 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5049 if (message == WM_CHAR || message == WM_SYSCHAR)
5051 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5053 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5054 goto found;
5057 else
5059 if(fVirt & FVIRTKEY)
5061 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5062 wParam, 0xff & HIWORD(lParam));
5064 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5065 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5067 else
5069 if (!(lParam & 0x01000000)) /* no special_key */
5071 if ((fVirt & FALT) && (lParam & 0x20000000))
5072 { /* ^^ ALT pressed */
5073 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5074 goto found;
5079 return FALSE;
5081 found:
5082 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5083 mesg = 1;
5084 else
5086 HMENU hMenu, hSubMenu, hSysMenu;
5087 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5089 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5090 hSysMenu = get_win_sys_menu( hWnd );
5092 /* find menu item and ask application to initialize it */
5093 /* 1. in the system menu */
5094 hSubMenu = hSysMenu;
5095 nPos = cmd;
5096 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5098 if (GetCapture())
5099 mesg = 2;
5100 if (!IsWindowEnabled(hWnd))
5101 mesg = 3;
5102 else
5104 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5105 if(hSubMenu != hSysMenu)
5107 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5108 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5109 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5111 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5114 else /* 2. in the window's menu */
5116 hSubMenu = hMenu;
5117 nPos = cmd;
5118 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5120 if (GetCapture())
5121 mesg = 2;
5122 if (!IsWindowEnabled(hWnd))
5123 mesg = 3;
5124 else
5126 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5127 if(hSubMenu != hMenu)
5129 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5130 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5131 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5133 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5138 if (mesg == 0)
5140 if (uSysStat != (UINT)-1)
5142 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5143 mesg=4;
5144 else
5145 mesg=WM_SYSCOMMAND;
5147 else
5149 if (uStat != (UINT)-1)
5151 if (IsIconic(hWnd))
5152 mesg=5;
5153 else
5155 if (uStat & (MF_DISABLED|MF_GRAYED))
5156 mesg=6;
5157 else
5158 mesg=WM_COMMAND;
5161 else
5162 mesg=WM_COMMAND;
5167 if( mesg==WM_COMMAND )
5169 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5170 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5172 else if( mesg==WM_SYSCOMMAND )
5174 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5175 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5177 else
5179 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5180 * #0: unknown (please report!)
5181 * #1: for WM_KEYUP,WM_SYSKEYUP
5182 * #2: mouse is captured
5183 * #3: window is disabled
5184 * #4: it's a disabled system menu option
5185 * #5: it's a menu option, but window is iconic
5186 * #6: it's a menu option, but disabled
5188 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5189 if(mesg==0)
5190 ERR_(accel)(" unknown reason - please report!\n");
5192 return TRUE;
5195 /**********************************************************************
5196 * TranslateAcceleratorA (USER32.@)
5197 * TranslateAccelerator (USER32.@)
5199 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5201 /* YES, Accel16! */
5202 LPACCEL16 lpAccelTbl;
5203 int i;
5204 WPARAM wParam;
5206 if (!hWnd || !msg) return 0;
5208 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5210 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5211 return 0;
5214 wParam = msg->wParam;
5216 switch (msg->message)
5218 case WM_KEYDOWN:
5219 case WM_SYSKEYDOWN:
5220 break;
5222 case WM_CHAR:
5223 case WM_SYSCHAR:
5225 char ch = LOWORD(wParam);
5226 WCHAR wch;
5227 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5228 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5230 break;
5232 default:
5233 return 0;
5236 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5237 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5238 i = 0;
5241 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5242 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5243 return 1;
5244 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5246 return 0;
5249 /**********************************************************************
5250 * TranslateAcceleratorW (USER32.@)
5252 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5254 /* YES, Accel16! */
5255 LPACCEL16 lpAccelTbl;
5256 int i;
5258 if (!hWnd || !msg) return 0;
5260 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5262 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5263 return 0;
5266 switch (msg->message)
5268 case WM_KEYDOWN:
5269 case WM_SYSKEYDOWN:
5270 case WM_CHAR:
5271 case WM_SYSCHAR:
5272 break;
5274 default:
5275 return 0;
5278 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5279 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5280 i = 0;
5283 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5284 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5285 return 1;
5286 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5288 return 0;