user32: ToUnicodeEx should return 0 for an unknown key.
[wine/dibdrv.git] / dlls / user / menu.c
blob59c8386cb6f0e29f64707532ab4ce93956ed0eaa
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 #include "windef.h"
49 #include "winbase.h"
50 #include "wingdi.h"
51 #include "winnls.h"
52 #include "wine/winbase16.h"
53 #include "wine/winuser16.h"
54 #include "wownt32.h"
55 #include "wine/server.h"
56 #include "wine/unicode.h"
57 #include "win.h"
58 #include "controls.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu);
63 WINE_DECLARE_DEBUG_CHANNEL(accel);
65 /* internal popup menu window messages */
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
70 /* Menu item structure */
71 typedef struct {
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType; /* Item type. */
74 UINT fState; /* Item state. */
75 UINT_PTR wID; /* Item id. */
76 HMENU hSubMenu; /* Pop-up menu. */
77 HBITMAP hCheckBit; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
79 LPWSTR text; /* Item text. */
80 ULONG_PTR dwItemData; /* Application defined. */
81 LPWSTR dwTypeData; /* depends on fMask */
82 HBITMAP hbmpItem; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect; /* Item area (relative to menu window) */
85 UINT xTab; /* X position of text after Tab */
86 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
87 * bitmap */
88 } MENUITEM;
90 /* Popup menu structure */
91 typedef struct {
92 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
93 WORD wMagic; /* Magic number */
94 WORD Width; /* Width of the whole menu */
95 WORD Height; /* Height of the whole menu */
96 UINT nItems; /* Number of items in the menu */
97 HWND hWnd; /* Window containing the menu */
98 MENUITEM *items; /* Array of menu items */
99 UINT FocusedItem; /* Currently focused item */
100 HWND hwndOwner; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling; /* Scroll arrows are active */
103 UINT nScrollPos; /* Current scroll position */
104 UINT nTotalHeight; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle; /* Extended menu style */
107 UINT cyMax; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack; /* brush for menu background */
109 DWORD dwContextHelpID;
110 DWORD dwMenuData; /* application defined value */
111 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
112 SIZE maxBmpSize; /* Maximum size of the bitmap items */
113 } POPUPMENU, *LPPOPUPMENU;
115 /* internal flags for menu tracking */
117 #define TF_ENDMENU 0x0001
118 #define TF_SUSPENDPOPUP 0x0002
119 #define TF_SKIPREMOVE 0x0004
121 typedef struct
123 UINT trackFlags;
124 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu; /* initial menu */
126 HWND hOwnerWnd; /* where notifications are sent */
127 POINT pt;
128 } MTRACKER;
130 #define MENU_MAGIC 0x554d /* 'MU' */
132 #define ITEM_PREV -1
133 #define ITEM_NEXT 1
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
138 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
139 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
141 /* Space between 2 columns */
142 #define MENU_COL_SPACE 4
144 /* top and bottom margins for popup menus */
145 #define MENU_TOP_MARGIN 3
146 #define MENU_BOTTOM_MARGIN 2
148 /* (other menu->FocusedItem values give the position of the focused item) */
149 #define NO_SELECTED_ITEM 0xffff
151 #define MENU_ITEM_TYPE(flags) \
152 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
154 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
155 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
156 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
158 #define IS_SYSTEM_MENU(menu) \
159 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
161 #define MENUITEMINFO_TYPE_MASK \
162 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
163 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
164 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
165 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
166 #define STATE_MASK (~TYPE_MASK)
167 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
169 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
171 static SIZE menucharsize;
172 static UINT ODitemheight; /* default owner drawn item height */
174 /* Use global popup window because there's no way 2 menus can
175 * be tracked at the same time. */
176 static HWND top_popup;
178 /* Flag set by EndMenu() to force an exit from menu tracking */
179 static BOOL fEndMenu = FALSE;
181 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
183 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
185 /*********************************************************************
186 * menu class descriptor
188 const struct builtin_class_descr MENU_builtin_class =
190 POPUPMENU_CLASS_ATOMA, /* name */
191 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
192 NULL, /* procA (winproc is Unicode only) */
193 PopupMenuWndProc, /* procW */
194 sizeof(HMENU), /* extra */
195 IDC_ARROW, /* cursor */
196 (HBRUSH)(COLOR_MENU+1) /* brush */
200 /***********************************************************************
201 * debug_print_menuitem
203 * Print a menuitem in readable form.
206 #define debug_print_menuitem(pre, mp, post) \
207 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
209 #define MENUOUT(text) \
210 TRACE("%s%s", (count++ ? "," : ""), (text))
212 #define MENUFLAG(bit,text) \
213 do { \
214 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
215 } while (0)
217 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
218 const char *postfix)
220 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
221 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
222 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
223 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
224 TRACE("%s ", prefix);
225 if (mp) {
226 UINT flags = mp->fType;
227 TRACE( "{ ID=0x%x", mp->wID);
228 if ( mp->hSubMenu)
229 TRACE( ", Sub=%p", mp->hSubMenu);
230 if (flags) {
231 int count = 0;
232 TRACE( ", fType=");
233 MENUFLAG( MFT_SEPARATOR, "sep");
234 MENUFLAG( MFT_OWNERDRAW, "own");
235 MENUFLAG( MFT_BITMAP, "bit");
236 MENUFLAG(MF_POPUP, "pop");
237 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
238 MENUFLAG(MFT_MENUBREAK, "brk");
239 MENUFLAG(MFT_RADIOCHECK, "radio");
240 MENUFLAG(MFT_RIGHTORDER, "rorder");
241 MENUFLAG(MF_SYSMENU, "sys");
242 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
243 if (flags)
244 TRACE( "+0x%x", flags);
246 flags = mp->fState;
247 if (flags) {
248 int count = 0;
249 TRACE( ", State=");
250 MENUFLAG(MFS_GRAYED, "grey");
251 MENUFLAG(MFS_DEFAULT, "default");
252 MENUFLAG(MFS_DISABLED, "dis");
253 MENUFLAG(MFS_CHECKED, "check");
254 MENUFLAG(MFS_HILITE, "hi");
255 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
256 MENUFLAG(MF_MOUSESELECT, "mouse");
257 if (flags)
258 TRACE( "+0x%x", flags);
260 if (mp->hCheckBit)
261 TRACE( ", Chk=%p", mp->hCheckBit);
262 if (mp->hUnCheckBit)
263 TRACE( ", Unc=%p", mp->hUnCheckBit);
264 if (mp->text)
265 TRACE( ", Text=%s", debugstr_w(mp->text));
266 if (mp->dwItemData)
267 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
268 if (mp->hbmpItem)
270 if( IS_MAGIC_BITMAP(mp->hbmpItem))
271 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
272 else
273 TRACE( ", hbitmap=%p", mp->hbmpItem);
275 TRACE( " }");
276 } else
277 TRACE( "NULL");
278 TRACE(" %s\n", postfix);
281 #undef MENUOUT
282 #undef MENUFLAG
285 /***********************************************************************
286 * MENU_GetMenu
288 * Validate the given menu handle and returns the menu structure pointer.
290 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
292 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
293 if (!menu || menu->wMagic != MENU_MAGIC)
295 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
296 menu = NULL;
298 return menu;
301 /***********************************************************************
302 * get_win_sys_menu
304 * Get the system menu of a window
306 static HMENU get_win_sys_menu( HWND hwnd )
308 HMENU ret = 0;
309 WND *win = WIN_GetPtr( hwnd );
310 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
312 ret = win->hSysMenu;
313 WIN_ReleasePtr( win );
315 return ret;
318 /***********************************************************************
319 * get_menu_font
321 static HFONT get_menu_font( BOOL bold )
323 static HFONT hMenuFont, hMenuFontBold;
325 HFONT ret = bold ? hMenuFontBold : hMenuFont;
327 if (!ret)
329 NONCLIENTMETRICSW ncm;
330 HFONT prev;
332 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
333 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
335 if (bold)
337 ncm.lfMenuFont.lfWeight += 300;
338 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
340 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
341 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
342 ret, NULL );
343 if (prev)
345 /* another thread beat us to it */
346 DeleteObject( ret );
347 ret = prev;
350 return ret;
353 /***********************************************************************
354 * get_arrow_bitmap
356 static HBITMAP get_arrow_bitmap(void)
358 static HBITMAP arrow_bitmap;
360 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
361 return arrow_bitmap;
364 /***********************************************************************
365 * get_down_arrow_bitmap
367 static HBITMAP get_down_arrow_bitmap(void)
369 static HBITMAP arrow_bitmap;
371 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
372 return arrow_bitmap;
375 /***********************************************************************
376 * get_down_arrow_inactive_bitmap
378 static HBITMAP get_down_arrow_inactive_bitmap(void)
380 static HBITMAP arrow_bitmap;
382 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
383 return arrow_bitmap;
386 /***********************************************************************
387 * get_up_arrow_bitmap
389 static HBITMAP get_up_arrow_bitmap(void)
391 static HBITMAP arrow_bitmap;
393 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
394 return arrow_bitmap;
397 /***********************************************************************
398 * get_up_arrow_inactive_bitmap
400 static HBITMAP get_up_arrow_inactive_bitmap(void)
402 static HBITMAP arrow_bitmap;
404 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
405 return arrow_bitmap;
408 /***********************************************************************
409 * MENU_CopySysPopup
411 * Return the default system menu.
413 static HMENU MENU_CopySysPopup(void)
415 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
416 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
418 if( hMenu ) {
419 POPUPMENU* menu = MENU_GetMenu(hMenu);
420 menu->wFlags |= MF_SYSMENU | MF_POPUP;
421 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
423 else
424 ERR("Unable to load default system menu\n" );
426 TRACE("returning %p.\n", hMenu );
428 return hMenu;
432 /**********************************************************************
433 * MENU_GetSysMenu
435 * Create a copy of the system menu. System menu in Windows is
436 * a special menu bar with the single entry - system menu popup.
437 * This popup is presented to the outside world as a "system menu".
438 * However, the real system menu handle is sometimes seen in the
439 * WM_MENUSELECT parameters (and Word 6 likes it this way).
441 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
443 HMENU hMenu;
445 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
446 if ((hMenu = CreateMenu()))
448 POPUPMENU *menu = MENU_GetMenu(hMenu);
449 menu->wFlags = MF_SYSMENU;
450 menu->hWnd = WIN_GetFullHandle( hWnd );
451 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
453 if (!hPopupMenu)
454 hPopupMenu = MENU_CopySysPopup();
456 if (hPopupMenu)
458 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
459 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
461 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
462 (UINT_PTR)hPopupMenu, NULL );
464 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
465 menu->items[0].fState = 0;
466 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
468 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
469 return hMenu;
471 DestroyMenu( hMenu );
473 ERR("failed to load system menu!\n");
474 return 0;
478 /***********************************************************************
479 * MENU_InitSysMenuPopup
481 * Grey the appropriate items in System menu.
483 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
485 BOOL gray;
487 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
488 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
489 gray = ((style & WS_MAXIMIZE) != 0);
490 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
491 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
492 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
493 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
494 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
496 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
497 gray = (clsStyle & CS_NOCLOSE) != 0;
499 /* The menu item must keep its state if it's disabled */
500 if(gray)
501 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
505 /******************************************************************************
507 * UINT MENU_GetStartOfNextColumn(
508 * HMENU hMenu )
510 *****************************************************************************/
512 static UINT MENU_GetStartOfNextColumn(
513 HMENU hMenu )
515 POPUPMENU *menu = MENU_GetMenu(hMenu);
516 UINT i;
518 if(!menu)
519 return NO_SELECTED_ITEM;
521 i = menu->FocusedItem + 1;
522 if( i == NO_SELECTED_ITEM )
523 return i;
525 for( ; i < menu->nItems; ++i ) {
526 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
527 return i;
530 return NO_SELECTED_ITEM;
534 /******************************************************************************
536 * UINT MENU_GetStartOfPrevColumn(
537 * HMENU hMenu )
539 *****************************************************************************/
541 static UINT MENU_GetStartOfPrevColumn(
542 HMENU hMenu )
544 POPUPMENU *menu = MENU_GetMenu(hMenu);
545 UINT i;
547 if( !menu )
548 return NO_SELECTED_ITEM;
550 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
551 return NO_SELECTED_ITEM;
553 /* Find the start of the column */
555 for(i = menu->FocusedItem; i != 0 &&
556 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
557 --i); /* empty */
559 if(i == 0)
560 return NO_SELECTED_ITEM;
562 for(--i; i != 0; --i) {
563 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
564 break;
567 TRACE("ret %d.\n", i );
569 return i;
574 /***********************************************************************
575 * MENU_FindItem
577 * Find a menu item. Return a pointer on the item, and modifies *hmenu
578 * in case the item was in a sub-menu.
580 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
582 POPUPMENU *menu;
583 MENUITEM *fallback = NULL;
584 UINT fallback_pos = 0;
585 UINT i;
587 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
588 if (wFlags & MF_BYPOSITION)
590 if (*nPos >= menu->nItems) return NULL;
591 return &menu->items[*nPos];
593 else
595 MENUITEM *item = menu->items;
596 for (i = 0; i < menu->nItems; i++, item++)
598 if (item->fType & MF_POPUP)
600 HMENU hsubmenu = item->hSubMenu;
601 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
602 if (subitem)
604 *hmenu = hsubmenu;
605 return subitem;
607 else if (item->wID == *nPos)
609 /* fallback to this item if nothing else found */
610 fallback_pos = i;
611 fallback = item;
614 else if (item->wID == *nPos)
616 *nPos = i;
617 return item;
622 if (fallback)
623 *nPos = fallback_pos;
625 return fallback;
628 /***********************************************************************
629 * MENU_FindSubMenu
631 * Find a Sub menu. Return the position of the submenu, and modifies
632 * *hmenu in case it is found in another sub-menu.
633 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
635 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
637 POPUPMENU *menu;
638 UINT i;
639 MENUITEM *item;
640 if (((*hmenu)==(HMENU)0xffff) ||
641 (!(menu = MENU_GetMenu(*hmenu))))
642 return NO_SELECTED_ITEM;
643 item = menu->items;
644 for (i = 0; i < menu->nItems; i++, item++) {
645 if(!(item->fType & MF_POPUP)) continue;
646 if (item->hSubMenu == hSubTarget) {
647 return i;
649 else {
650 HMENU hsubmenu = item->hSubMenu;
651 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
652 if (pos != NO_SELECTED_ITEM) {
653 *hmenu = hsubmenu;
654 return pos;
658 return NO_SELECTED_ITEM;
661 /***********************************************************************
662 * MENU_FreeItemData
664 static void MENU_FreeItemData( MENUITEM* item )
666 /* delete text */
667 HeapFree( GetProcessHeap(), 0, item->text );
670 /***********************************************************************
671 * MENU_AdjustMenuItemRect
673 * Adjust menu item rectangle according to scrolling state.
675 static void
676 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
678 if (menu->bScrolling)
680 UINT arrow_bitmap_width, arrow_bitmap_height;
681 BITMAP bmp;
683 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
684 arrow_bitmap_width = bmp.bmWidth;
685 arrow_bitmap_height = bmp.bmHeight;
686 rect->top += arrow_bitmap_height - menu->nScrollPos;
687 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
692 /***********************************************************************
693 * MENU_FindItemByCoords
695 * Find the item at the specified coordinates (screen coords). Does
696 * not work for child windows and therefore should not be called for
697 * an arbitrary system menu.
699 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
700 POINT pt, UINT *pos )
702 MENUITEM *item;
703 UINT i;
704 RECT wrect;
705 RECT rect;
707 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
708 pt.x -= wrect.left;pt.y -= wrect.top;
709 item = menu->items;
710 for (i = 0; i < menu->nItems; i++, item++)
712 rect = item->rect;
713 MENU_AdjustMenuItemRect(menu, &rect);
714 if ((pt.x >= rect.left) && (pt.x < rect.right) &&
715 (pt.y >= rect.top) && (pt.y < rect.bottom))
717 if (pos) *pos = i;
718 return item;
721 return NULL;
725 /***********************************************************************
726 * MENU_FindItemByKey
728 * Find the menu item selected by a key press.
729 * Return item id, -1 if none, -2 if we should close the menu.
731 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
732 WCHAR key, BOOL forceMenuChar )
734 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
736 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
738 if (hmenu)
740 POPUPMENU *menu = MENU_GetMenu( hmenu );
741 MENUITEM *item = menu->items;
742 LRESULT menuchar;
744 if( !forceMenuChar )
746 UINT i;
748 for (i = 0; i < menu->nItems; i++, item++)
750 if( item->text)
752 WCHAR *p = item->text - 2;
755 p = strchrW (p + 2, '&');
757 while (p != NULL && p [1] == '&');
758 if (p && (toupperW(p[1]) == toupperW(key))) return i;
762 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
763 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
764 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
765 if (HIWORD(menuchar) == 1) return (UINT)(-2);
767 return (UINT)(-1);
771 /***********************************************************************
772 * MENU_GetBitmapItemSize
774 * Get the size of a bitmap item.
776 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
777 HWND hwndOwner)
779 BITMAP bm;
780 HBITMAP bmp = lpitem->hbmpItem;
782 size->cx = size->cy = 0;
784 /* check if there is a magic menu item associated with this item */
785 switch( (INT_PTR) bmp )
787 case (INT_PTR)HBMMENU_CALLBACK:
789 MEASUREITEMSTRUCT measItem;
790 measItem.CtlType = ODT_MENU;
791 measItem.CtlID = 0;
792 measItem.itemID = lpitem->wID;
793 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
794 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
795 measItem.itemData = lpitem->dwItemData;
796 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
797 size->cx = measItem.itemWidth;
798 size->cy = measItem.itemHeight;
799 return;
801 break;
802 case (INT_PTR)HBMMENU_SYSTEM:
803 if (lpitem->dwItemData)
805 bmp = (HBITMAP)lpitem->dwItemData;
806 break;
808 /* fall through */
809 case (INT_PTR)HBMMENU_MBAR_RESTORE:
810 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
811 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
812 case (INT_PTR)HBMMENU_MBAR_CLOSE:
813 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
814 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
815 size->cy = size->cx;
816 return;
817 case (INT_PTR)HBMMENU_POPUP_CLOSE:
818 case (INT_PTR)HBMMENU_POPUP_RESTORE:
819 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
820 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
821 FIXME("Magic %p not implemented\n", bmp );
822 return;
824 if (GetObjectW(bmp, sizeof(bm), &bm ))
826 size->cx = bm.bmWidth;
827 size->cy = bm.bmHeight;
831 /***********************************************************************
832 * MENU_DrawBitmapItem
834 * Draw a bitmap item.
836 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
837 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
839 BITMAP bm;
840 DWORD rop;
841 HDC hdcMem;
842 HBITMAP bmp;
843 int w = rect->right - rect->left;
844 int h = rect->bottom - rect->top;
845 int bmp_xoffset = 0;
846 int left, top;
847 HBITMAP hbmToDraw = lpitem->hbmpItem;
848 bmp = hbmToDraw;
850 /* Check if there is a magic menu item associated with this item */
851 if (IS_MAGIC_BITMAP(hbmToDraw))
853 UINT flags = 0;
854 RECT r;
856 switch((INT_PTR)hbmToDraw)
858 case (INT_PTR)HBMMENU_SYSTEM:
859 if (lpitem->dwItemData)
861 bmp = (HBITMAP)lpitem->dwItemData;
862 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
864 else
866 static HBITMAP hBmpSysMenu;
868 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
869 bmp = hBmpSysMenu;
870 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
871 /* only use right half of the bitmap */
872 bmp_xoffset = bm.bmWidth / 2;
873 bm.bmWidth -= bmp_xoffset;
875 goto got_bitmap;
876 case (INT_PTR)HBMMENU_MBAR_RESTORE:
877 flags = DFCS_CAPTIONRESTORE;
878 break;
879 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
880 flags = DFCS_CAPTIONMIN;
881 break;
882 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
883 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
884 break;
885 case (INT_PTR)HBMMENU_MBAR_CLOSE:
886 flags = DFCS_CAPTIONCLOSE;
887 break;
888 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
889 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
890 break;
891 case (INT_PTR)HBMMENU_CALLBACK:
893 DRAWITEMSTRUCT drawItem;
894 drawItem.CtlType = ODT_MENU;
895 drawItem.CtlID = 0;
896 drawItem.itemID = lpitem->wID;
897 drawItem.itemAction = odaction;
898 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
899 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
900 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
901 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
902 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
903 drawItem.hwndItem = (HWND)hmenu;
904 drawItem.hDC = hdc;
905 drawItem.itemData = lpitem->dwItemData;
906 drawItem.rcItem = *rect;
907 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
908 return;
910 break;
911 case (INT_PTR)HBMMENU_POPUP_CLOSE:
912 case (INT_PTR)HBMMENU_POPUP_RESTORE:
913 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
914 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
915 default:
916 FIXME("Magic %p not implemented\n", hbmToDraw);
917 return;
919 r = *rect;
920 InflateRect( &r, -1, -1 );
921 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
922 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
923 return;
926 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
928 got_bitmap:
929 hdcMem = CreateCompatibleDC( hdc );
930 SelectObject( hdcMem, bmp );
932 /* handle fontsize > bitmap_height */
933 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
934 left=rect->left;
935 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
936 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
937 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
938 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
939 DeleteDC( hdcMem );
943 /***********************************************************************
944 * MENU_CalcItemSize
946 * Calculate the size of the menu item and store it in lpitem->rect.
948 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
949 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
951 WCHAR *p;
952 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
953 UINT arrow_bitmap_width;
954 BITMAP bm;
955 INT itemheight;
957 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
958 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
959 (menuBar ? " (MenuBar)" : ""));
961 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
962 arrow_bitmap_width = bm.bmWidth;
964 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
965 if( !menucharsize.cx ) {
966 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
967 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
968 * but it is unlikely an application will depend on that */
969 ODitemheight = HIWORD( GetDialogBaseUnits());
972 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
974 if (lpitem->fType & MF_OWNERDRAW)
976 MEASUREITEMSTRUCT mis;
977 mis.CtlType = ODT_MENU;
978 mis.CtlID = 0;
979 mis.itemID = lpitem->wID;
980 mis.itemData = lpitem->dwItemData;
981 mis.itemHeight = ODitemheight;
982 mis.itemWidth = 0;
983 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
984 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
985 * width of a menufont character to the width of an owner-drawn menu.
987 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
988 if (menuBar) {
989 /* under at least win95 you seem to be given a standard
990 height for the menu and the height value is ignored */
991 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
992 } else
993 lpitem->rect.bottom += mis.itemHeight;
995 TRACE("id=%04x size=%dx%d\n",
996 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
997 lpitem->rect.bottom-lpitem->rect.top);
998 return;
1001 if (lpitem->fType & MF_SEPARATOR)
1003 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1004 if( !menuBar)
1005 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1006 return;
1009 itemheight = 0;
1010 lpitem->xTab = 0;
1012 if (!menuBar) {
1013 if (lpitem->hbmpItem) {
1014 SIZE size;
1016 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1017 /* Keep the size of the bitmap in callback mode to be able
1018 * to draw it correctly */
1019 lpitem->bmpsize = size;
1020 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1021 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1022 lpitem->rect.right += size.cx + 2;
1023 itemheight = size.cy + 2;
1025 if( !(lppop->dwStyle & MNS_NOCHECK))
1026 lpitem->rect.right += check_bitmap_width;
1027 lpitem->rect.right += 4 + menucharsize.cx;
1028 lpitem->xTab = lpitem->rect.right;
1029 lpitem->rect.right += arrow_bitmap_width;
1030 } else if (lpitem->hbmpItem) { /* menuBar */
1031 SIZE size;
1033 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1034 lpitem->bmpsize = size;
1035 lpitem->rect.right += size.cx;
1036 if( lpitem->text) lpitem->rect.right += 2;
1037 itemheight = size.cy;
1040 /* it must be a text item - unless it's the system menu */
1041 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1042 HFONT hfontOld = NULL;
1043 RECT rc = lpitem->rect;
1044 LONG txtheight, txtwidth;
1046 if ( lpitem->fState & MFS_DEFAULT ) {
1047 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1049 if (menuBar) {
1050 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1051 DT_SINGLELINE|DT_CALCRECT);
1052 lpitem->rect.right += rc.right - rc.left;
1053 itemheight = max( max( itemheight, txtheight),
1054 GetSystemMetrics( SM_CYMENU) - 1);
1055 lpitem->rect.right += 2 * menucharsize.cx;
1056 } else {
1057 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1058 RECT tmprc = rc;
1059 LONG tmpheight;
1060 int n = (int)( p - lpitem->text);
1061 /* Item contains a tab (only meaningful in popup menus) */
1062 /* get text size before the tab */
1063 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1064 DT_SINGLELINE|DT_CALCRECT);
1065 txtwidth = rc.right - rc.left;
1066 p += 1; /* advance past the Tab */
1067 /* get text size after the tab */
1068 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1069 DT_SINGLELINE|DT_CALCRECT);
1070 lpitem->xTab += txtwidth;
1071 txtheight = max( txtheight, tmpheight);
1072 txtwidth += menucharsize.cx + /* space for the tab */
1073 tmprc.right - tmprc.left; /* space for the short cut */
1074 } else {
1075 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1076 DT_SINGLELINE|DT_CALCRECT);
1077 txtwidth = rc.right - rc.left;
1078 lpitem->xTab += txtwidth;
1080 lpitem->rect.right += 2 + txtwidth;
1081 itemheight = max( itemheight,
1082 max( txtheight + 2, menucharsize.cy + 4));
1084 if (hfontOld) SelectObject (hdc, hfontOld);
1085 } else if( menuBar) {
1086 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1088 lpitem->rect.bottom += itemheight;
1089 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1093 /***********************************************************************
1094 * MENU_GetMaxPopupHeight
1096 static UINT
1097 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1099 if (lppop->cyMax)
1100 return lppop->cyMax;
1101 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1105 /***********************************************************************
1106 * MENU_PopupMenuCalcSize
1108 * Calculate the size of a popup menu.
1110 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1112 MENUITEM *lpitem;
1113 HDC hdc;
1114 int start, i;
1115 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1117 lppop->Width = lppop->Height = 0;
1118 if (lppop->nItems == 0) return;
1119 hdc = GetDC( 0 );
1121 SelectObject( hdc, get_menu_font(FALSE));
1123 start = 0;
1124 maxX = 2 + 1;
1126 lppop->maxBmpSize.cx = 0;
1127 lppop->maxBmpSize.cy = 0;
1129 while (start < lppop->nItems)
1131 lpitem = &lppop->items[start];
1132 orgX = maxX;
1133 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1134 orgX += MENU_COL_SPACE;
1135 orgY = MENU_TOP_MARGIN;
1137 maxTab = maxTabWidth = 0;
1138 /* Parse items until column break or end of menu */
1139 for (i = start; i < lppop->nItems; i++, lpitem++)
1141 if ((i != start) &&
1142 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1144 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1145 maxX = max( maxX, lpitem->rect.right );
1146 orgY = lpitem->rect.bottom;
1147 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1149 maxTab = max( maxTab, lpitem->xTab );
1150 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1154 /* Finish the column (set all items to the largest width found) */
1155 maxX = max( maxX, maxTab + maxTabWidth );
1156 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1158 lpitem->rect.right = maxX;
1159 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1160 lpitem->xTab = maxTab;
1163 lppop->Height = max( lppop->Height, orgY );
1166 lppop->Width = maxX;
1168 /* space for 3d border */
1169 lppop->Height += MENU_BOTTOM_MARGIN;
1170 lppop->Width += 2;
1172 /* Adjust popup height if it exceeds maximum */
1173 maxHeight = MENU_GetMaxPopupHeight(lppop);
1174 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1175 if (lppop->Height >= maxHeight)
1177 lppop->Height = maxHeight;
1178 lppop->bScrolling = TRUE;
1180 else
1182 lppop->bScrolling = FALSE;
1185 ReleaseDC( 0, hdc );
1189 /***********************************************************************
1190 * MENU_MenuBarCalcSize
1192 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1193 * height is off by 1 pixel which causes lengthy window relocations when
1194 * active document window is maximized/restored.
1196 * Calculate the size of the menu bar.
1198 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1199 LPPOPUPMENU lppop, HWND hwndOwner )
1201 MENUITEM *lpitem;
1202 int start, i, orgX, orgY, maxY, helpPos;
1204 if ((lprect == NULL) || (lppop == NULL)) return;
1205 if (lppop->nItems == 0) return;
1206 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1207 lppop->Width = lprect->right - lprect->left;
1208 lppop->Height = 0;
1209 maxY = lprect->top+1;
1210 start = 0;
1211 helpPos = -1;
1212 lppop->maxBmpSize.cx = 0;
1213 lppop->maxBmpSize.cy = 0;
1214 while (start < lppop->nItems)
1216 lpitem = &lppop->items[start];
1217 orgX = lprect->left;
1218 orgY = maxY;
1220 /* Parse items until line break or end of menu */
1221 for (i = start; i < lppop->nItems; i++, lpitem++)
1223 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1224 if ((i != start) &&
1225 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1227 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1228 debug_print_menuitem (" item: ", lpitem, "");
1229 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1231 if (lpitem->rect.right > lprect->right)
1233 if (i != start) break;
1234 else lpitem->rect.right = lprect->right;
1236 maxY = max( maxY, lpitem->rect.bottom );
1237 orgX = lpitem->rect.right;
1240 /* Finish the line (set all items to the largest height found) */
1241 while (start < i) lppop->items[start++].rect.bottom = maxY;
1244 lprect->bottom = maxY;
1245 lppop->Height = lprect->bottom - lprect->top;
1247 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1248 /* the last item (if several lines, only move the last line) */
1249 lpitem = &lppop->items[lppop->nItems-1];
1250 orgY = lpitem->rect.top;
1251 orgX = lprect->right;
1252 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1253 if ( (helpPos==-1) || (helpPos>i) )
1254 break; /* done */
1255 if (lpitem->rect.top != orgY) break; /* Other line */
1256 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1257 lpitem->rect.left += orgX - lpitem->rect.right;
1258 lpitem->rect.right = orgX;
1259 orgX = lpitem->rect.left;
1264 /***********************************************************************
1265 * MENU_DrawScrollArrows
1267 * Draw scroll arrows.
1269 static void
1270 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1272 HDC hdcMem = CreateCompatibleDC(hdc);
1273 HBITMAP hOrigBitmap;
1274 UINT arrow_bitmap_width, arrow_bitmap_height;
1275 BITMAP bmp;
1276 RECT rect;
1278 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1279 arrow_bitmap_width = bmp.bmWidth;
1280 arrow_bitmap_height = bmp.bmHeight;
1283 if (lppop->nScrollPos)
1284 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1285 else
1286 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1287 rect.left = 0;
1288 rect.top = 0;
1289 rect.right = lppop->Width;
1290 rect.bottom = arrow_bitmap_height;
1291 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1292 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1293 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1294 rect.top = lppop->Height - arrow_bitmap_height;
1295 rect.bottom = lppop->Height;
1296 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1297 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1298 SelectObject(hdcMem, get_down_arrow_bitmap());
1299 else
1300 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1301 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1302 lppop->Height - arrow_bitmap_height,
1303 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1304 SelectObject(hdcMem, hOrigBitmap);
1305 DeleteDC(hdcMem);
1309 /***********************************************************************
1310 * draw_popup_arrow
1312 * Draws the popup-menu arrow.
1314 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1315 UINT arrow_bitmap_height)
1317 HDC hdcMem = CreateCompatibleDC( hdc );
1318 HBITMAP hOrigBitmap;
1320 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1321 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1322 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1323 arrow_bitmap_width, arrow_bitmap_height,
1324 hdcMem, 0, 0, SRCCOPY );
1325 SelectObject( hdcMem, hOrigBitmap );
1326 DeleteDC( hdcMem );
1328 /***********************************************************************
1329 * MENU_DrawMenuItem
1331 * Draw a single menu item.
1333 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1334 UINT height, BOOL menuBar, UINT odaction )
1336 RECT rect;
1337 BOOL flat_menu = FALSE;
1338 int bkgnd;
1339 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1340 POPUPMENU *menu = MENU_GetMenu(hmenu);
1341 RECT bmprc;
1343 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1345 if (!menuBar) {
1346 BITMAP bmp;
1347 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1348 arrow_bitmap_width = bmp.bmWidth;
1349 arrow_bitmap_height = bmp.bmHeight;
1352 if (lpitem->fType & MF_SYSMENU)
1354 if( !IsIconic(hwnd) )
1355 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1356 return;
1359 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1360 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1362 /* Setup colors */
1364 if (lpitem->fState & MF_HILITE)
1366 if(menuBar && !flat_menu) {
1367 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1368 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1369 } else {
1370 if(lpitem->fState & MF_GRAYED)
1371 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1372 else
1373 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1374 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1377 else
1379 if (lpitem->fState & MF_GRAYED)
1380 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1381 else
1382 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1383 SetBkColor( hdc, GetSysColor( bkgnd ) );
1386 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1387 rect = lpitem->rect;
1388 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1390 if (lpitem->fType & MF_OWNERDRAW)
1393 ** Experimentation under Windows reveals that an owner-drawn
1394 ** menu is given the rectangle which includes the space it requested
1395 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1396 ** and a popup-menu arrow. This is the value of lpitem->rect.
1397 ** Windows will leave all drawing to the application except for
1398 ** the popup-menu arrow. Windows always draws that itself, after
1399 ** the menu owner has finished drawing.
1401 DRAWITEMSTRUCT dis;
1403 dis.CtlType = ODT_MENU;
1404 dis.CtlID = 0;
1405 dis.itemID = lpitem->wID;
1406 dis.itemData = lpitem->dwItemData;
1407 dis.itemState = 0;
1408 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1409 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1410 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1411 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1412 dis.hwndItem = (HWND)hmenu;
1413 dis.hDC = hdc;
1414 dis.rcItem = rect;
1415 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1416 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1417 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1418 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1419 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1420 /* Draw the popup-menu arrow */
1421 if (lpitem->fType & MF_POPUP)
1422 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1423 arrow_bitmap_height);
1424 return;
1427 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1429 if (lpitem->fState & MF_HILITE)
1431 if (flat_menu)
1433 InflateRect (&rect, -1, -1);
1434 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1435 InflateRect (&rect, 1, 1);
1436 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1438 else
1440 if(menuBar)
1441 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1442 else
1443 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1446 else
1447 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1449 SetBkMode( hdc, TRANSPARENT );
1451 /* vertical separator */
1452 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1454 HPEN oldPen;
1455 RECT rc = rect;
1457 rc.left -= MENU_COL_SPACE / 2 + 1;
1458 rc.top = 3;
1459 rc.bottom = height - 3;
1460 if (flat_menu)
1462 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1463 MoveToEx( hdc, rc.left, rc.top, NULL );
1464 LineTo( hdc, rc.left, rc.bottom );
1465 SelectObject( hdc, oldPen );
1467 else
1468 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1471 /* horizontal separator */
1472 if (lpitem->fType & MF_SEPARATOR)
1474 HPEN oldPen;
1475 RECT rc = rect;
1477 rc.left++;
1478 rc.right--;
1479 rc.top = ( rc.top + rc.bottom) / 2;
1480 if (flat_menu)
1482 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1483 MoveToEx( hdc, rc.left, rc.top, NULL );
1484 LineTo( hdc, rc.right, rc.top );
1485 SelectObject( hdc, oldPen );
1487 else
1488 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1489 return;
1492 /* helper lines for debugging */
1493 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1494 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1495 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1496 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1499 if (lpitem->hbmpItem) {
1500 /* calculate the bitmap rectangle in coordinates relative
1501 * to the item rectangle */
1502 if( menuBar) {
1503 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1504 bmprc.left = 3;
1505 else
1506 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1507 } else {
1508 bmprc.left = 4;
1509 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1510 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1512 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1513 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1514 bmprc.top = 0;
1515 else
1516 bmprc.top = (lpitem->rect.bottom - lpitem->rect.top -
1517 lpitem->bmpsize.cy) / 2;
1518 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1521 if (!menuBar)
1523 HBITMAP bm;
1524 INT y = rect.top + rect.bottom;
1525 RECT rc = rect;
1526 int checked = FALSE;
1527 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1528 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1529 /* Draw the check mark
1531 * FIXME:
1532 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1534 if( !(menu->dwStyle & MNS_NOCHECK)) {
1535 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1536 lpitem->hUnCheckBit;
1537 if (bm) /* we have a custom bitmap */
1539 HDC hdcMem = CreateCompatibleDC( hdc );
1541 SelectObject( hdcMem, bm );
1542 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1543 check_bitmap_width, check_bitmap_height,
1544 hdcMem, 0, 0, SRCCOPY );
1545 DeleteDC( hdcMem );
1546 checked = TRUE;
1548 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1550 RECT r;
1551 HBITMAP bm = CreateBitmap( check_bitmap_width,
1552 check_bitmap_height, 1, 1, NULL );
1553 HDC hdcMem = CreateCompatibleDC( hdc );
1555 SelectObject( hdcMem, bm );
1556 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1557 DrawFrameControl( hdcMem, &r, DFC_MENU,
1558 (lpitem->fType & MFT_RADIOCHECK) ?
1559 DFCS_MENUBULLET : DFCS_MENUCHECK );
1560 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1561 hdcMem, 0, 0, SRCCOPY );
1562 DeleteDC( hdcMem );
1563 DeleteObject( bm );
1564 checked = TRUE;
1567 if( lpitem->hbmpItem &&
1568 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1569 POINT origorg;
1570 /* some applications make this assumption on the DC's origin */
1571 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1572 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1573 odaction, FALSE);
1574 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1576 /* Draw the popup-menu arrow */
1577 if (lpitem->fType & MF_POPUP)
1578 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1579 arrow_bitmap_height);
1580 rect.left += 4;
1581 if( !(menu->dwStyle & MNS_NOCHECK))
1582 rect.left += check_bitmap_width;
1583 rect.right -= arrow_bitmap_width;
1585 else if( lpitem->hbmpItem)
1586 { /* Draw the bitmap */
1587 POINT origorg;
1589 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1590 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1591 odaction, menuBar);
1592 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1594 /* process text if present */
1595 if (lpitem->text)
1597 register int i;
1598 HFONT hfontOld = 0;
1600 UINT uFormat = (menuBar) ?
1601 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1602 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1604 if( !(menu->dwStyle & MNS_CHECKORBMP))
1605 rect.left += menu->maxBmpSize.cx;
1607 if ( lpitem->fState & MFS_DEFAULT )
1609 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1612 if (menuBar) {
1613 if( lpitem->hbmpItem)
1614 rect.left += lpitem->bmpsize.cx;
1615 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1616 rect.left += menucharsize.cx;
1617 rect.right -= menucharsize.cx;
1620 for (i = 0; lpitem->text[i]; i++)
1621 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1622 break;
1624 if(lpitem->fState & MF_GRAYED)
1626 if (!(lpitem->fState & MF_HILITE) )
1628 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1629 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1630 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1631 --rect.left; --rect.top; --rect.right; --rect.bottom;
1633 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1636 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1638 /* paint the shortcut text */
1639 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1641 if (lpitem->text[i] == '\t')
1643 rect.left = lpitem->xTab;
1644 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1646 else
1648 rect.right = lpitem->xTab;
1649 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1652 if(lpitem->fState & MF_GRAYED)
1654 if (!(lpitem->fState & MF_HILITE) )
1656 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1657 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1658 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1659 --rect.left; --rect.top; --rect.right; --rect.bottom;
1661 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1663 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1666 if (hfontOld)
1667 SelectObject (hdc, hfontOld);
1672 /***********************************************************************
1673 * MENU_DrawPopupMenu
1675 * Paint a popup menu.
1677 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1679 HBRUSH hPrevBrush = 0;
1680 RECT rect;
1682 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1684 GetClientRect( hwnd, &rect );
1686 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1687 && (SelectObject( hdc, get_menu_font(FALSE))))
1689 HPEN hPrevPen;
1691 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1693 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1694 if( hPrevPen )
1696 POPUPMENU *menu;
1697 BOOL flat_menu = FALSE;
1699 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1700 if (flat_menu)
1701 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1702 else
1703 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1705 if( (menu = MENU_GetMenu( hmenu )))
1707 /* draw menu items */
1708 if( menu->nItems)
1710 MENUITEM *item;
1711 UINT u;
1713 item = menu->items;
1714 for( u = menu->nItems; u > 0; u--, item++)
1715 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1716 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1718 /* draw scroll arrows */
1719 if (menu->bScrolling)
1720 MENU_DrawScrollArrows(menu, hdc);
1722 } else
1724 SelectObject( hdc, hPrevBrush );
1729 /***********************************************************************
1730 * MENU_DrawMenuBar
1732 * Paint a menu bar. Returns the height of the menu bar.
1733 * called from [windows/nonclient.c]
1735 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1736 BOOL suppress_draw)
1738 LPPOPUPMENU lppop;
1739 HFONT hfontOld = 0;
1740 HMENU hMenu = GetMenu(hwnd);
1742 lppop = MENU_GetMenu( hMenu );
1743 if (lppop == NULL || lprect == NULL)
1745 return GetSystemMetrics(SM_CYMENU);
1748 if (suppress_draw)
1750 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1752 if (lppop->Height == 0)
1753 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1755 lprect->bottom = lprect->top + lppop->Height;
1757 if (hfontOld) SelectObject( hDC, hfontOld);
1758 return lppop->Height;
1760 else
1761 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1765 /***********************************************************************
1766 * MENU_ShowPopup
1768 * Display a popup menu.
1770 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1771 INT x, INT y, INT xanchor, INT yanchor )
1773 POPUPMENU *menu;
1774 INT width, height;
1775 POINT pt;
1776 HMONITOR monitor;
1777 MONITORINFO info;
1779 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1780 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1782 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1783 if (menu->FocusedItem != NO_SELECTED_ITEM)
1785 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1786 menu->FocusedItem = NO_SELECTED_ITEM;
1789 /* store the owner for DrawItem */
1790 menu->hwndOwner = hwndOwner;
1792 menu->nScrollPos = 0;
1793 MENU_PopupMenuCalcSize( menu, hwndOwner );
1795 /* adjust popup menu pos so that it fits within the desktop */
1797 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1798 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1800 /* FIXME: should use item rect */
1801 pt.x = x;
1802 pt.y = y;
1803 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1804 info.cbSize = sizeof(info);
1805 GetMonitorInfoW( monitor, &info );
1806 if( x + width > info.rcWork.right)
1808 if( xanchor && x >= width - xanchor )
1809 x -= width - xanchor;
1811 if( x + width > info.rcWork.right)
1812 x = info.rcWork.right - width;
1814 if( x < info.rcWork.left ) x = info.rcWork.left;
1816 if( y + height > info.rcWork.bottom)
1818 if( yanchor && y >= height + yanchor )
1819 y -= height + yanchor;
1821 if( y + height > info.rcWork.bottom)
1822 y = info.rcWork.bottom - height;
1824 if( y < info.rcWork.top ) y = info.rcWork.top;
1826 /* NOTE: In Windows, top menu popup is not owned. */
1827 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1828 WS_POPUP, x, y, width, height,
1829 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1830 (LPVOID)hmenu );
1831 if( !menu->hWnd ) return FALSE;
1832 if (!top_popup) top_popup = menu->hWnd;
1834 /* Display the window */
1836 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1837 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1838 UpdateWindow( menu->hWnd );
1839 return TRUE;
1843 /***********************************************************************
1844 * MENU_EnsureMenuItemVisible
1846 static void
1847 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1849 if (lppop->bScrolling)
1851 MENUITEM *item = &lppop->items[wIndex];
1852 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1853 UINT nOldPos = lppop->nScrollPos;
1854 RECT rc;
1855 UINT arrow_bitmap_height;
1856 BITMAP bmp;
1858 GetClientRect(lppop->hWnd, &rc);
1860 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1861 arrow_bitmap_height = bmp.bmHeight;
1863 rc.top += arrow_bitmap_height;
1864 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1866 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1867 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1870 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1871 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1872 MENU_DrawScrollArrows(lppop, hdc);
1874 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1876 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1877 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1878 MENU_DrawScrollArrows(lppop, hdc);
1884 /***********************************************************************
1885 * MENU_SelectItem
1887 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1888 BOOL sendMenuSelect, HMENU topmenu )
1890 LPPOPUPMENU lppop;
1891 HDC hdc;
1893 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1895 lppop = MENU_GetMenu( hmenu );
1896 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1898 if (lppop->FocusedItem == wIndex) return;
1899 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1900 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1901 if (!top_popup) top_popup = lppop->hWnd;
1903 SelectObject( hdc, get_menu_font(FALSE));
1905 /* Clear previous highlighted item */
1906 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1908 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1909 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1910 lppop->Height, !(lppop->wFlags & MF_POPUP),
1911 ODA_SELECT );
1914 /* Highlight new item (if any) */
1915 lppop->FocusedItem = wIndex;
1916 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1918 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1919 lppop->items[wIndex].fState |= MF_HILITE;
1920 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1921 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1922 &lppop->items[wIndex], lppop->Height,
1923 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1925 if (sendMenuSelect)
1927 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1928 SendMessageW( hwndOwner, WM_MENUSELECT,
1929 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1930 ip->fType | ip->fState |
1931 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1934 else if (sendMenuSelect) {
1935 if(topmenu){
1936 int pos;
1937 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1938 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1939 MENUITEM *ip = &ptm->items[pos];
1940 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1941 ip->fType | ip->fState |
1942 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1946 ReleaseDC( lppop->hWnd, hdc );
1950 /***********************************************************************
1951 * MENU_MoveSelection
1953 * Moves currently selected item according to the offset parameter.
1954 * If there is no selection then it should select the last item if
1955 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1957 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1959 INT i;
1960 POPUPMENU *menu;
1962 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1964 menu = MENU_GetMenu( hmenu );
1965 if ((!menu) || (!menu->items)) return;
1967 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1969 if( menu->nItems == 1 ) return; else
1970 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1971 ; i += offset)
1972 if (!(menu->items[i].fType & MF_SEPARATOR))
1974 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1975 return;
1979 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1980 i >= 0 && i < menu->nItems ; i += offset)
1981 if (!(menu->items[i].fType & MF_SEPARATOR))
1983 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1984 return;
1989 /**********************************************************************
1990 * MENU_SetItemData
1992 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1993 * ModifyMenu().
1995 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1996 LPCWSTR str )
1998 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1999 TRACE("flags=%x str=%p\n", flags, str);
2001 if (IS_STRING_ITEM(flags))
2003 LPWSTR prevText = item->text;
2004 if (!str)
2006 flags |= MF_SEPARATOR;
2007 item->text = NULL;
2009 else
2011 LPWSTR text;
2012 /* Item beginning with a backspace is a help item */
2013 if (*str == '\b')
2015 flags |= MF_HELP;
2016 str++;
2018 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2019 return FALSE;
2020 strcpyW( text, str );
2021 item->text = text;
2023 item->hbmpItem = NULL;
2024 HeapFree( GetProcessHeap(), 0, prevText );
2026 else if(( flags & MFT_BITMAP)) {
2027 item->hbmpItem = HBITMAP_32(LOWORD(str));
2028 /* setting bitmap clears text */
2029 HeapFree( GetProcessHeap(), 0, item->text );
2030 item->text = NULL;
2033 if (flags & MF_OWNERDRAW)
2034 item->dwItemData = (DWORD_PTR)str;
2035 else
2036 item->dwItemData = 0;
2038 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2039 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2041 if (flags & MF_POPUP)
2043 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2044 if (menu) menu->wFlags |= MF_POPUP;
2045 else
2047 item->wID = 0;
2048 item->hSubMenu = 0;
2049 item->fType = 0;
2050 item->fState = 0;
2051 return FALSE;
2055 item->wID = id;
2056 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2058 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2059 flags |= MF_POPUP; /* keep popup */
2061 item->fType = flags & TYPE_MASK;
2062 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2063 for ModifyMenu, but Windows accepts it */
2064 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2066 /* Don't call SetRectEmpty here! */
2068 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2069 return TRUE;
2073 /**********************************************************************
2074 * MENU_InsertItem
2076 * Insert (allocate) a new item into a menu.
2078 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2080 MENUITEM *newItems;
2081 POPUPMENU *menu;
2083 if (!(menu = MENU_GetMenu(hMenu)))
2084 return NULL;
2086 /* Find where to insert new item */
2088 if (flags & MF_BYPOSITION) {
2089 if (pos > menu->nItems)
2090 pos = menu->nItems;
2091 } else {
2092 if (!MENU_FindItem( &hMenu, &pos, flags ))
2093 pos = menu->nItems;
2094 else {
2095 if (!(menu = MENU_GetMenu( hMenu )))
2096 return NULL;
2100 /* Create new items array */
2102 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2103 if (!newItems)
2105 WARN("allocation failed\n" );
2106 return NULL;
2108 if (menu->nItems > 0)
2110 /* Copy the old array into the new one */
2111 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2112 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2113 (menu->nItems-pos)*sizeof(MENUITEM) );
2114 HeapFree( GetProcessHeap(), 0, menu->items );
2116 menu->items = newItems;
2117 menu->nItems++;
2118 memset( &newItems[pos], 0, sizeof(*newItems) );
2119 menu->Height = 0; /* force size recalculate */
2120 return &newItems[pos];
2124 /**********************************************************************
2125 * MENU_ParseResource
2127 * Parse a standard menu resource and add items to the menu.
2128 * Return a pointer to the end of the resource.
2130 * NOTE: flags is equivalent to the mtOption field
2132 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2134 WORD flags, id = 0;
2135 LPCSTR str;
2136 BOOL end_flag;
2140 flags = GET_WORD(res);
2141 end_flag = flags & MF_END;
2142 /* Remove MF_END because it has the same value as MF_HILITE */
2143 flags &= ~MF_END;
2144 res += sizeof(WORD);
2145 if (!(flags & MF_POPUP))
2147 id = GET_WORD(res);
2148 res += sizeof(WORD);
2150 str = res;
2151 if (!unicode) res += strlen(str) + 1;
2152 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2153 if (flags & MF_POPUP)
2155 HMENU hSubMenu = CreatePopupMenu();
2156 if (!hSubMenu) return NULL;
2157 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2158 return NULL;
2159 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2160 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2162 else /* Not a popup */
2164 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2165 else AppendMenuW( hMenu, flags, id,
2166 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2168 } while (!end_flag);
2169 return res;
2173 /**********************************************************************
2174 * MENUEX_ParseResource
2176 * Parse an extended menu resource and add items to the menu.
2177 * Return a pointer to the end of the resource.
2179 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2181 WORD resinfo;
2182 do {
2183 MENUITEMINFOW mii;
2185 mii.cbSize = sizeof(mii);
2186 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2187 mii.fType = GET_DWORD(res);
2188 res += sizeof(DWORD);
2189 mii.fState = GET_DWORD(res);
2190 res += sizeof(DWORD);
2191 mii.wID = GET_DWORD(res);
2192 res += sizeof(DWORD);
2193 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2194 res += sizeof(WORD);
2195 /* Align the text on a word boundary. */
2196 res += (~((UINT_PTR)res - 1)) & 1;
2197 mii.dwTypeData = (LPWSTR) res;
2198 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2199 /* Align the following fields on a dword boundary. */
2200 res += (~((UINT_PTR)res - 1)) & 3;
2202 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2203 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2205 if (resinfo & 1) { /* Pop-up? */
2206 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2207 res += sizeof(DWORD);
2208 mii.hSubMenu = CreatePopupMenu();
2209 if (!mii.hSubMenu)
2210 return NULL;
2211 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2212 DestroyMenu(mii.hSubMenu);
2213 return NULL;
2215 mii.fMask |= MIIM_SUBMENU;
2216 mii.fType |= MF_POPUP;
2218 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2220 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2221 mii.wID, mii.fType);
2222 mii.fType |= MF_SEPARATOR;
2224 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2225 } while (!(resinfo & MF_END));
2226 return res;
2230 /***********************************************************************
2231 * MENU_GetSubPopup
2233 * Return the handle of the selected sub-popup menu (if any).
2235 static HMENU MENU_GetSubPopup( HMENU hmenu )
2237 POPUPMENU *menu;
2238 MENUITEM *item;
2240 menu = MENU_GetMenu( hmenu );
2242 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2244 item = &menu->items[menu->FocusedItem];
2245 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2246 return item->hSubMenu;
2247 return 0;
2251 /***********************************************************************
2252 * MENU_HideSubPopups
2254 * Hide the sub-popup menus of this menu.
2256 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2257 BOOL sendMenuSelect )
2259 POPUPMENU *menu = MENU_GetMenu( hmenu );
2261 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2263 if (menu && top_popup)
2265 HMENU hsubmenu;
2266 POPUPMENU *submenu;
2267 MENUITEM *item;
2269 if (menu->FocusedItem != NO_SELECTED_ITEM)
2271 item = &menu->items[menu->FocusedItem];
2272 if (!(item->fType & MF_POPUP) ||
2273 !(item->fState & MF_MOUSESELECT)) return;
2274 item->fState &= ~MF_MOUSESELECT;
2275 hsubmenu = item->hSubMenu;
2276 } else return;
2278 submenu = MENU_GetMenu( hsubmenu );
2279 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2280 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2281 DestroyWindow( submenu->hWnd );
2282 submenu->hWnd = 0;
2287 /***********************************************************************
2288 * MENU_ShowSubPopup
2290 * Display the sub-menu of the selected item of this menu.
2291 * Return the handle of the submenu, or hmenu if no submenu to display.
2293 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2294 BOOL selectFirst, UINT wFlags )
2296 RECT rect;
2297 POPUPMENU *menu;
2298 MENUITEM *item;
2299 HDC hdc;
2301 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2303 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2305 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2307 item = &menu->items[menu->FocusedItem];
2308 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2309 return hmenu;
2311 /* message must be sent before using item,
2312 because nearly everything may be changed by the application ! */
2314 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2315 if (!(wFlags & TPM_NONOTIFY))
2316 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2317 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2319 item = &menu->items[menu->FocusedItem];
2320 rect = item->rect;
2322 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2323 if (!(item->fState & MF_HILITE))
2325 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2326 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2328 SelectObject( hdc, get_menu_font(FALSE));
2330 item->fState |= MF_HILITE;
2331 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2332 ReleaseDC( menu->hWnd, hdc );
2334 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2335 item->rect = rect;
2337 item->fState |= MF_MOUSESELECT;
2339 if (IS_SYSTEM_MENU(menu))
2341 MENU_InitSysMenuPopup(item->hSubMenu,
2342 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2343 GetClassLongW( menu->hWnd, GCL_STYLE));
2345 NC_GetSysPopupPos( menu->hWnd, &rect );
2346 rect.top = rect.bottom;
2347 rect.right = GetSystemMetrics(SM_CXSIZE);
2348 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2350 else
2352 GetWindowRect( menu->hWnd, &rect );
2353 if (menu->wFlags & MF_POPUP)
2355 RECT rc = item->rect;
2357 MENU_AdjustMenuItemRect(menu, &rc);
2359 /* The first item in the popup menu has to be at the
2360 same y position as the focused menu item */
2361 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2362 rect.top += rc.top - MENU_TOP_MARGIN;
2363 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2364 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2365 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2367 else
2369 rect.left += item->rect.left;
2370 rect.top += item->rect.bottom;
2371 rect.right = item->rect.right - item->rect.left;
2372 rect.bottom = item->rect.bottom - item->rect.top;
2376 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2377 rect.left, rect.top, rect.right, rect.bottom );
2378 if (selectFirst)
2379 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2380 return item->hSubMenu;
2385 /**********************************************************************
2386 * MENU_IsMenuActive
2388 HWND MENU_IsMenuActive(void)
2390 return top_popup;
2393 /***********************************************************************
2394 * MENU_PtMenu
2396 * Walks menu chain trying to find a menu pt maps to.
2398 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2400 POPUPMENU *menu = MENU_GetMenu( hMenu );
2401 UINT item = menu->FocusedItem;
2402 HMENU ret;
2404 /* try subpopup first (if any) */
2405 ret = (item != NO_SELECTED_ITEM &&
2406 (menu->items[item].fType & MF_POPUP) &&
2407 (menu->items[item].fState & MF_MOUSESELECT))
2408 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2410 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2412 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2413 if( menu->wFlags & MF_POPUP )
2415 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2417 else if (ht == HTSYSMENU)
2418 ret = get_win_sys_menu( menu->hWnd );
2419 else if (ht == HTMENU)
2420 ret = GetMenu( menu->hWnd );
2422 return ret;
2425 /***********************************************************************
2426 * MENU_ExecFocusedItem
2428 * Execute a menu item (for instance when user pressed Enter).
2429 * Return the wID of the executed item. Otherwise, -1 indicating
2430 * that no menu item was executed, -2 if a popup is shown;
2431 * Have to receive the flags for the TrackPopupMenu options to avoid
2432 * sending unwanted message.
2435 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2437 MENUITEM *item;
2438 POPUPMENU *menu = MENU_GetMenu( hMenu );
2440 TRACE("%p hmenu=%p\n", pmt, hMenu);
2442 if (!menu || !menu->nItems ||
2443 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2445 item = &menu->items[menu->FocusedItem];
2447 TRACE("hMenu %p wID %08x hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2449 if (!(item->fType & MF_POPUP))
2451 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2453 /* If TPM_RETURNCMD is set you return the id, but
2454 do not send a message to the owner */
2455 if(!(wFlags & TPM_RETURNCMD))
2457 if( menu->wFlags & MF_SYSMENU )
2458 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2459 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2460 else
2462 if (menu->dwStyle & MNS_NOTIFYBYPOS)
2463 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2464 (LPARAM)hMenu);
2465 else
2466 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2469 return item->wID;
2472 else
2474 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2475 return -2;
2478 return -1;
2481 /***********************************************************************
2482 * MENU_SwitchTracking
2484 * Helper function for menu navigation routines.
2486 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2488 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2489 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2491 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2493 if( pmt->hTopMenu != hPtMenu &&
2494 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2496 /* both are top level menus (system and menu-bar) */
2497 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2498 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2499 pmt->hTopMenu = hPtMenu;
2501 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2502 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2506 /***********************************************************************
2507 * MENU_ButtonDown
2509 * Return TRUE if we can go on with menu tracking.
2511 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2513 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2515 if (hPtMenu)
2517 UINT id = 0;
2518 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2519 MENUITEM *item;
2521 if( IS_SYSTEM_MENU(ptmenu) )
2522 item = ptmenu->items;
2523 else
2524 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2526 if( item )
2528 if( ptmenu->FocusedItem != id )
2529 MENU_SwitchTracking( pmt, hPtMenu, id );
2531 /* If the popup menu is not already "popped" */
2532 if(!(item->fState & MF_MOUSESELECT ))
2534 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2537 return TRUE;
2539 /* Else the click was on the menu bar, finish the tracking */
2541 return FALSE;
2544 /***********************************************************************
2545 * MENU_ButtonUp
2547 * Return the value of MENU_ExecFocusedItem if
2548 * the selected item was not a popup. Else open the popup.
2549 * A -1 return value indicates that we go on with menu tracking.
2552 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2554 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2556 if (hPtMenu)
2558 UINT id = 0;
2559 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2560 MENUITEM *item;
2562 if( IS_SYSTEM_MENU(ptmenu) )
2563 item = ptmenu->items;
2564 else
2565 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2567 if( item && (ptmenu->FocusedItem == id ))
2569 debug_print_menuitem ("FocusedItem: ", item, "");
2571 if( !(item->fType & MF_POPUP) )
2573 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2574 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2575 return executedMenuId;
2578 /* If we are dealing with the top-level menu */
2579 /* and this is a click on an already "popped" item: */
2580 /* Stop the menu tracking and close the opened submenus */
2581 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2582 return 0;
2584 ptmenu->bTimeToHide = TRUE;
2586 return -1;
2590 /***********************************************************************
2591 * MENU_MouseMove
2593 * Return TRUE if we can go on with menu tracking.
2595 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2597 UINT id = NO_SELECTED_ITEM;
2598 POPUPMENU *ptmenu = NULL;
2600 if( hPtMenu )
2602 ptmenu = MENU_GetMenu( hPtMenu );
2603 if( IS_SYSTEM_MENU(ptmenu) )
2604 id = 0;
2605 else
2606 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2609 if( id == NO_SELECTED_ITEM )
2611 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2612 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2615 else if( ptmenu->FocusedItem != id )
2617 MENU_SwitchTracking( pmt, hPtMenu, id );
2618 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2620 return TRUE;
2624 /***********************************************************************
2625 * MENU_SetCapture
2627 static void MENU_SetCapture( HWND hwnd )
2629 HWND previous = 0;
2631 SERVER_START_REQ( set_capture_window )
2633 req->handle = hwnd;
2634 req->flags = CAPTURE_MENU;
2635 if (!wine_server_call_err( req ))
2637 previous = reply->previous;
2638 hwnd = reply->full_handle;
2641 SERVER_END_REQ;
2643 if (previous && previous != hwnd)
2644 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2648 /***********************************************************************
2649 * MENU_DoNextMenu
2651 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2653 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2655 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2657 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2658 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2660 MDINEXTMENU next_menu;
2661 HMENU hNewMenu;
2662 HWND hNewWnd;
2663 UINT id = 0;
2665 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2666 next_menu.hmenuNext = 0;
2667 next_menu.hwndNext = 0;
2668 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2670 TRACE("%p [%p] -> %p [%p]\n",
2671 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2673 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2675 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2676 hNewWnd = pmt->hOwnerWnd;
2677 if( IS_SYSTEM_MENU(menu) )
2679 /* switch to the menu bar */
2681 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2683 if( vk == VK_LEFT )
2685 menu = MENU_GetMenu( hNewMenu );
2686 id = menu->nItems - 1;
2689 else if (style & WS_SYSMENU )
2691 /* switch to the system menu */
2692 hNewMenu = get_win_sys_menu( hNewWnd );
2694 else return FALSE;
2696 else /* application returned a new menu to switch to */
2698 hNewMenu = next_menu.hmenuNext;
2699 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2701 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2703 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2705 if (style & WS_SYSMENU &&
2706 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2708 /* get the real system menu */
2709 hNewMenu = get_win_sys_menu(hNewWnd);
2711 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2713 /* FIXME: Not sure what to do here;
2714 * perhaps try to track hNewMenu as a popup? */
2716 TRACE(" -- got confused.\n");
2717 return FALSE;
2720 else return FALSE;
2723 if( hNewMenu != pmt->hTopMenu )
2725 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2726 FALSE, 0 );
2727 if( pmt->hCurrentMenu != pmt->hTopMenu )
2728 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2731 if( hNewWnd != pmt->hOwnerWnd )
2733 pmt->hOwnerWnd = hNewWnd;
2734 MENU_SetCapture( pmt->hOwnerWnd );
2737 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2738 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2740 return TRUE;
2742 return FALSE;
2745 /***********************************************************************
2746 * MENU_SuspendPopup
2748 * The idea is not to show the popup if the next input message is
2749 * going to hide it anyway.
2751 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2753 MSG msg;
2755 msg.hwnd = pmt->hOwnerWnd;
2757 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2758 pmt->trackFlags |= TF_SKIPREMOVE;
2760 switch( uMsg )
2762 case WM_KEYDOWN:
2763 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2764 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2766 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2767 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2768 if( msg.message == WM_KEYDOWN &&
2769 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2771 pmt->trackFlags |= TF_SUSPENDPOPUP;
2772 return TRUE;
2775 break;
2778 /* failures go through this */
2779 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2780 return FALSE;
2783 /***********************************************************************
2784 * MENU_KeyEscape
2786 * Handle a VK_ESCAPE key event in a menu.
2788 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2790 BOOL bEndMenu = TRUE;
2792 if (pmt->hCurrentMenu != pmt->hTopMenu)
2794 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2796 if (menu->wFlags & MF_POPUP)
2798 HMENU hmenutmp, hmenuprev;
2800 hmenuprev = hmenutmp = pmt->hTopMenu;
2802 /* close topmost popup */
2803 while (hmenutmp != pmt->hCurrentMenu)
2805 hmenuprev = hmenutmp;
2806 hmenutmp = MENU_GetSubPopup( hmenuprev );
2809 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2810 pmt->hCurrentMenu = hmenuprev;
2811 bEndMenu = FALSE;
2815 return bEndMenu;
2818 /***********************************************************************
2819 * MENU_KeyLeft
2821 * Handle a VK_LEFT key event in a menu.
2823 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2825 POPUPMENU *menu;
2826 HMENU hmenutmp, hmenuprev;
2827 UINT prevcol;
2829 hmenuprev = hmenutmp = pmt->hTopMenu;
2830 menu = MENU_GetMenu( hmenutmp );
2832 /* Try to move 1 column left (if possible) */
2833 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2834 NO_SELECTED_ITEM ) {
2836 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2837 prevcol, TRUE, 0 );
2838 return;
2841 /* close topmost popup */
2842 while (hmenutmp != pmt->hCurrentMenu)
2844 hmenuprev = hmenutmp;
2845 hmenutmp = MENU_GetSubPopup( hmenuprev );
2848 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2849 pmt->hCurrentMenu = hmenuprev;
2851 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2853 /* move menu bar selection if no more popups are left */
2855 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2856 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2858 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2860 /* A sublevel menu was displayed - display the next one
2861 * unless there is another displacement coming up */
2863 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2864 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2865 pmt->hTopMenu, TRUE, wFlags);
2871 /***********************************************************************
2872 * MENU_KeyRight
2874 * Handle a VK_RIGHT key event in a menu.
2876 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2878 HMENU hmenutmp;
2879 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2880 UINT nextcol;
2882 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2883 pmt->hCurrentMenu,
2884 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2885 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2887 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2889 /* If already displaying a popup, try to display sub-popup */
2891 hmenutmp = pmt->hCurrentMenu;
2892 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2894 /* if subpopup was displayed then we are done */
2895 if (hmenutmp != pmt->hCurrentMenu) return;
2898 /* Check to see if there's another column */
2899 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2900 NO_SELECTED_ITEM ) {
2901 TRACE("Going to %d.\n", nextcol );
2902 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2903 nextcol, TRUE, 0 );
2904 return;
2907 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2909 if( pmt->hCurrentMenu != pmt->hTopMenu )
2911 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2912 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2913 } else hmenutmp = 0;
2915 /* try to move to the next item */
2916 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2917 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2919 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2920 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2921 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2922 pmt->hTopMenu, TRUE, wFlags);
2926 /***********************************************************************
2927 * MENU_TrackMenu
2929 * Menu tracking code.
2931 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2932 HWND hwnd, const RECT *lprect )
2934 MSG msg;
2935 POPUPMENU *menu;
2936 BOOL fRemove;
2937 INT executedMenuId = -1;
2938 MTRACKER mt;
2939 BOOL enterIdleSent = FALSE;
2941 mt.trackFlags = 0;
2942 mt.hCurrentMenu = hmenu;
2943 mt.hTopMenu = hmenu;
2944 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2945 mt.pt.x = x;
2946 mt.pt.y = y;
2948 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2949 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2951 fEndMenu = FALSE;
2952 if (!(menu = MENU_GetMenu( hmenu )))
2954 WARN("Invalid menu handle %p\n", hmenu);
2955 SetLastError(ERROR_INVALID_MENU_HANDLE);
2956 return FALSE;
2959 if (wFlags & TPM_BUTTONDOWN)
2961 /* Get the result in order to start the tracking or not */
2962 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2963 fEndMenu = !fRemove;
2966 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2968 MENU_SetCapture( mt.hOwnerWnd );
2970 while (!fEndMenu)
2972 menu = MENU_GetMenu( mt.hCurrentMenu );
2973 if (!menu) /* sometimes happens if I do a window manager close */
2974 break;
2976 /* we have to keep the message in the queue until it's
2977 * clear that menu loop is not over yet. */
2979 for (;;)
2981 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2983 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2984 /* remove the message from the queue */
2985 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2987 else
2989 if (!enterIdleSent)
2991 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2992 enterIdleSent = TRUE;
2993 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2995 WaitMessage();
2999 /* check if EndMenu() tried to cancel us, by posting this message */
3000 if(msg.message == WM_CANCELMODE)
3002 /* we are now out of the loop */
3003 fEndMenu = TRUE;
3005 /* remove the message from the queue */
3006 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3008 /* break out of internal loop, ala ESCAPE */
3009 break;
3012 TranslateMessage( &msg );
3013 mt.pt = msg.pt;
3015 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3016 enterIdleSent=FALSE;
3018 fRemove = FALSE;
3019 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3022 * Use the mouse coordinates in lParam instead of those in the MSG
3023 * struct to properly handle synthetic messages. They are already
3024 * in screen coordinates.
3026 mt.pt.x = (short)LOWORD(msg.lParam);
3027 mt.pt.y = (short)HIWORD(msg.lParam);
3029 /* Find a menu for this mouse event */
3030 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3032 switch(msg.message)
3034 /* no WM_NC... messages in captured state */
3036 case WM_RBUTTONDBLCLK:
3037 case WM_RBUTTONDOWN:
3038 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3039 /* fall through */
3040 case WM_LBUTTONDBLCLK:
3041 case WM_LBUTTONDOWN:
3042 /* If the message belongs to the menu, removes it from the queue */
3043 /* Else, end menu tracking */
3044 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3045 fEndMenu = !fRemove;
3046 break;
3048 case WM_RBUTTONUP:
3049 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3050 /* fall through */
3051 case WM_LBUTTONUP:
3052 /* Check if a menu was selected by the mouse */
3053 if (hmenu)
3055 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3056 TRACE("executedMenuId %d\n", executedMenuId);
3058 /* End the loop if executedMenuId is an item ID */
3059 /* or if the job was done (executedMenuId = 0). */
3060 fEndMenu = fRemove = (executedMenuId != -1);
3062 /* No menu was selected by the mouse */
3063 /* if the function was called by TrackPopupMenu, continue
3064 with the menu tracking. If not, stop it */
3065 else
3066 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3068 break;
3070 case WM_MOUSEMOVE:
3071 /* the selected menu item must be changed every time */
3072 /* the mouse moves. */
3074 if (hmenu)
3075 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3077 } /* switch(msg.message) - mouse */
3079 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3081 fRemove = TRUE; /* Keyboard messages are always removed */
3082 switch(msg.message)
3084 case WM_KEYDOWN:
3085 case WM_SYSKEYDOWN:
3086 switch(msg.wParam)
3088 case VK_MENU:
3089 fEndMenu = TRUE;
3090 break;
3092 case VK_HOME:
3093 case VK_END:
3094 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3095 NO_SELECTED_ITEM, FALSE, 0 );
3096 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3097 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3098 break;
3100 case VK_UP:
3101 case VK_DOWN: /* If on menu bar, pull-down the menu */
3103 menu = MENU_GetMenu( mt.hCurrentMenu );
3104 if (!(menu->wFlags & MF_POPUP))
3105 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3106 else /* otherwise try to move selection */
3107 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3108 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3109 break;
3111 case VK_LEFT:
3112 MENU_KeyLeft( &mt, wFlags );
3113 break;
3115 case VK_RIGHT:
3116 MENU_KeyRight( &mt, wFlags );
3117 break;
3119 case VK_ESCAPE:
3120 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3121 break;
3123 case VK_F1:
3125 HELPINFO hi;
3126 hi.cbSize = sizeof(HELPINFO);
3127 hi.iContextType = HELPINFO_MENUITEM;
3128 if (menu->FocusedItem == NO_SELECTED_ITEM)
3129 hi.iCtrlId = 0;
3130 else
3131 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3132 hi.hItemHandle = hmenu;
3133 hi.dwContextId = menu->dwContextHelpID;
3134 hi.MousePos = msg.pt;
3135 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3136 break;
3139 default:
3140 break;
3142 break; /* WM_KEYDOWN */
3144 case WM_CHAR:
3145 case WM_SYSCHAR:
3147 UINT pos;
3149 if (msg.wParam == '\r' || msg.wParam == ' ')
3151 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3152 fEndMenu = (executedMenuId != -2);
3154 break;
3157 /* Hack to avoid control chars. */
3158 /* We will find a better way real soon... */
3159 if (msg.wParam < 32) break;
3161 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3162 LOWORD(msg.wParam), FALSE );
3163 if (pos == (UINT)-2) fEndMenu = TRUE;
3164 else if (pos == (UINT)-1) MessageBeep(0);
3165 else
3167 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3168 TRUE, 0 );
3169 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3170 fEndMenu = (executedMenuId != -2);
3173 break;
3174 } /* switch(msg.message) - kbd */
3176 else
3178 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3179 DispatchMessageW( &msg );
3180 continue;
3183 if (!fEndMenu) fRemove = TRUE;
3185 /* finally remove message from the queue */
3187 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3188 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3189 else mt.trackFlags &= ~TF_SKIPREMOVE;
3192 MENU_SetCapture(0); /* release the capture */
3194 /* If dropdown is still painted and the close box is clicked on
3195 then the menu will be destroyed as part of the DispatchMessage above.
3196 This will then invalidate the menu handle in mt.hTopMenu. We should
3197 check for this first. */
3198 if( IsMenu( mt.hTopMenu ) )
3200 menu = MENU_GetMenu( mt.hTopMenu );
3202 if( IsWindow( mt.hOwnerWnd ) )
3204 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3206 if (menu && (menu->wFlags & MF_POPUP))
3208 DestroyWindow( menu->hWnd );
3209 menu->hWnd = 0;
3211 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3212 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3215 /* Reset the variable for hiding menu */
3216 if( menu ) menu->bTimeToHide = FALSE;
3219 /* The return value is only used by TrackPopupMenu */
3220 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3221 if (executedMenuId == -1) executedMenuId = 0;
3222 return executedMenuId;
3225 /***********************************************************************
3226 * MENU_InitTracking
3228 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3230 POPUPMENU *menu;
3232 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3234 HideCaret(0);
3236 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3237 if (!(wFlags & TPM_NONOTIFY))
3238 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3240 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3242 if (!(wFlags & TPM_NONOTIFY))
3244 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3245 /* If an app changed/recreated menu bar entries in WM_INITMENU
3246 * menu sizes will be recalculated once the menu created/shown.
3250 /* This makes the menus of applications built with Delphi work.
3251 * It also enables menus to be displayed in more than one window,
3252 * but there are some bugs left that need to be fixed in this case.
3254 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3256 return TRUE;
3258 /***********************************************************************
3259 * MENU_ExitTracking
3261 static BOOL MENU_ExitTracking(HWND hWnd)
3263 TRACE("hwnd=%p\n", hWnd);
3265 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3266 ShowCaret(0);
3267 top_popup = 0;
3268 return TRUE;
3271 /***********************************************************************
3272 * MENU_TrackMouseMenuBar
3274 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3276 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3278 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3279 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3281 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3283 if (IsMenu(hMenu))
3285 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3286 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3287 MENU_ExitTracking(hWnd);
3292 /***********************************************************************
3293 * MENU_TrackKbdMenuBar
3295 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3297 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3299 UINT uItem = NO_SELECTED_ITEM;
3300 HMENU hTrackMenu;
3301 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3303 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3305 /* find window that has a menu */
3307 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3308 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3310 /* check if we have to track a system menu */
3312 hTrackMenu = GetMenu( hwnd );
3313 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3315 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3316 hTrackMenu = get_win_sys_menu( hwnd );
3317 uItem = 0;
3318 wParam |= HTSYSMENU; /* prevent item lookup */
3321 if (!IsMenu( hTrackMenu )) return;
3323 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3325 if( wChar && wChar != ' ' )
3327 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3328 if ( uItem >= (UINT)(-2) )
3330 if( uItem == (UINT)(-1) ) MessageBeep(0);
3331 /* schedule end of menu tracking */
3332 wFlags |= TF_ENDMENU;
3333 goto track_menu;
3337 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3339 if (wParam & HTSYSMENU)
3341 /* prevent sysmenu activation for managed windows on Alt down/up */
3342 if (GetPropA( hwnd, "__wine_x11_managed" ))
3343 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3345 else
3347 if( uItem == NO_SELECTED_ITEM )
3348 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3349 else
3350 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3353 track_menu:
3354 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3355 MENU_ExitTracking( hwnd );
3359 /**********************************************************************
3360 * TrackPopupMenu (USER32.@)
3362 * Like the win32 API, the function return the command ID only if the
3363 * flag TPM_RETURNCMD is on.
3366 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3367 INT nReserved, HWND hWnd, const RECT *lpRect )
3369 BOOL ret = FALSE;
3371 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3372 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3374 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3376 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3377 if (!(wFlags & TPM_NONOTIFY))
3378 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3380 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3381 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3382 MENU_ExitTracking(hWnd);
3384 return ret;
3387 /**********************************************************************
3388 * TrackPopupMenuEx (USER32.@)
3390 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3391 HWND hWnd, LPTPMPARAMS lpTpm )
3393 FIXME("not fully implemented\n" );
3394 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3395 lpTpm ? &lpTpm->rcExclude : NULL );
3398 /***********************************************************************
3399 * PopupMenuWndProc
3401 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3403 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3405 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3407 switch(message)
3409 case WM_CREATE:
3411 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3412 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3413 return 0;
3416 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3417 return MA_NOACTIVATE;
3419 case WM_PAINT:
3421 PAINTSTRUCT ps;
3422 BeginPaint( hwnd, &ps );
3423 MENU_DrawPopupMenu( hwnd, ps.hdc,
3424 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3425 EndPaint( hwnd, &ps );
3426 return 0;
3428 case WM_ERASEBKGND:
3429 return 1;
3431 case WM_DESTROY:
3432 /* zero out global pointer in case resident popup window was destroyed. */
3433 if (hwnd == top_popup) top_popup = 0;
3434 break;
3436 case WM_SHOWWINDOW:
3438 if( wParam )
3440 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3442 else
3443 SetWindowLongPtrW( hwnd, 0, 0 );
3444 break;
3446 case MM_SETMENUHANDLE:
3447 SetWindowLongPtrW( hwnd, 0, wParam );
3448 break;
3450 case MM_GETMENUHANDLE:
3451 return GetWindowLongPtrW( hwnd, 0 );
3453 default:
3454 return DefWindowProcW( hwnd, message, wParam, lParam );
3456 return 0;
3460 /***********************************************************************
3461 * MENU_GetMenuBarHeight
3463 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3465 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3466 INT orgX, INT orgY )
3468 HDC hdc;
3469 RECT rectBar;
3470 LPPOPUPMENU lppop;
3472 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3474 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3476 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3477 SelectObject( hdc, get_menu_font(FALSE));
3478 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3479 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3480 ReleaseDC( hwnd, hdc );
3481 return lppop->Height;
3485 /*******************************************************************
3486 * ChangeMenuA (USER32.@)
3488 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3489 UINT id, UINT flags )
3491 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3492 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3493 id, data );
3494 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3495 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3496 id, data );
3497 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3498 flags & MF_BYPOSITION ? pos : id,
3499 flags & ~MF_REMOVE );
3500 /* Default: MF_INSERT */
3501 return InsertMenuA( hMenu, pos, flags, id, data );
3505 /*******************************************************************
3506 * ChangeMenuW (USER32.@)
3508 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3509 UINT id, UINT flags )
3511 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3512 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3513 id, data );
3514 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3515 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3516 id, data );
3517 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3518 flags & MF_BYPOSITION ? pos : id,
3519 flags & ~MF_REMOVE );
3520 /* Default: MF_INSERT */
3521 return InsertMenuW( hMenu, pos, flags, id, data );
3525 /*******************************************************************
3526 * CheckMenuItem (USER32.@)
3528 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3530 MENUITEM *item;
3531 DWORD ret;
3533 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3534 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3535 ret = item->fState & MF_CHECKED;
3536 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3537 else item->fState &= ~MF_CHECKED;
3538 return ret;
3542 /**********************************************************************
3543 * EnableMenuItem (USER32.@)
3545 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3547 UINT oldflags;
3548 MENUITEM *item;
3549 POPUPMENU *menu;
3551 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3553 /* Get the Popupmenu to access the owner menu */
3554 if (!(menu = MENU_GetMenu(hMenu)))
3555 return (UINT)-1;
3557 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3558 return (UINT)-1;
3560 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3561 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3563 /* If the close item in the system menu change update the close button */
3564 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3566 if (menu->hSysMenuOwner != 0)
3568 RECT rc;
3569 POPUPMENU* parentMenu;
3571 /* Get the parent menu to access*/
3572 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3573 return (UINT)-1;
3575 /* Refresh the frame to reflect the change */
3576 GetWindowRect(parentMenu->hWnd, &rc);
3577 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3578 rc.bottom = 0;
3579 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3583 return oldflags;
3587 /*******************************************************************
3588 * GetMenuStringA (USER32.@)
3590 INT WINAPI GetMenuStringA(
3591 HMENU hMenu, /* [in] menuhandle */
3592 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3593 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3594 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3595 UINT wFlags /* [in] MF_ flags */
3597 MENUITEM *item;
3599 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3600 if (str && nMaxSiz) str[0] = '\0';
3601 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3602 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3603 return 0;
3605 if (!item->text) return 0;
3606 if (!str || !nMaxSiz) return strlenW(item->text);
3607 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3608 str[nMaxSiz-1] = 0;
3609 TRACE("returning '%s'\n", str );
3610 return strlen(str);
3614 /*******************************************************************
3615 * GetMenuStringW (USER32.@)
3617 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3618 LPWSTR str, INT nMaxSiz, UINT wFlags )
3620 MENUITEM *item;
3622 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3623 if (str && nMaxSiz) str[0] = '\0';
3624 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3625 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3626 return 0;
3628 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3629 if( !(item->text)) {
3630 str[0] = 0;
3631 return 0;
3633 lstrcpynW( str, item->text, nMaxSiz );
3634 return strlenW(str);
3638 /**********************************************************************
3639 * HiliteMenuItem (USER32.@)
3641 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3642 UINT wHilite )
3644 LPPOPUPMENU menu;
3645 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3646 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3647 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3648 if (menu->FocusedItem == wItemID) return TRUE;
3649 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3650 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3651 return TRUE;
3655 /**********************************************************************
3656 * GetMenuState (USER32.@)
3658 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3660 MENUITEM *item;
3661 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3662 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3663 debug_print_menuitem (" item: ", item, "");
3664 if (item->fType & MF_POPUP)
3666 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3667 if (!menu) return -1;
3668 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3670 else
3672 /* We used to (from way back then) mask the result to 0xff. */
3673 /* I don't know why and it seems wrong as the documented */
3674 /* return flag MF_SEPARATOR is outside that mask. */
3675 return (item->fType | item->fState);
3680 /**********************************************************************
3681 * GetMenuItemCount (USER32.@)
3683 INT WINAPI GetMenuItemCount( HMENU hMenu )
3685 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3686 if (!menu) return -1;
3687 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3688 return menu->nItems;
3692 /**********************************************************************
3693 * GetMenuItemID (USER32.@)
3695 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3697 MENUITEM * lpmi;
3699 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3700 if (lpmi->fType & MF_POPUP) return -1;
3701 return lpmi->wID;
3706 /*******************************************************************
3707 * InsertMenuW (USER32.@)
3709 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3710 UINT_PTR id, LPCWSTR str )
3712 MENUITEM *item;
3714 if (IS_STRING_ITEM(flags) && str)
3715 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3716 hMenu, pos, flags, id, debugstr_w(str) );
3717 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3718 hMenu, pos, flags, id, str );
3720 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3722 if (!(MENU_SetItemData( item, flags, id, str )))
3724 RemoveMenu( hMenu, pos, flags );
3725 return FALSE;
3728 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3729 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3731 item->hCheckBit = item->hUnCheckBit = 0;
3732 return TRUE;
3736 /*******************************************************************
3737 * InsertMenuA (USER32.@)
3739 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3740 UINT_PTR id, LPCSTR str )
3742 BOOL ret = FALSE;
3744 if (IS_STRING_ITEM(flags) && str)
3746 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3747 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3748 if (newstr)
3750 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3751 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3752 HeapFree( GetProcessHeap(), 0, newstr );
3754 return ret;
3756 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3760 /*******************************************************************
3761 * AppendMenuA (USER32.@)
3763 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3764 UINT_PTR id, LPCSTR data )
3766 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3770 /*******************************************************************
3771 * AppendMenuW (USER32.@)
3773 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3774 UINT_PTR id, LPCWSTR data )
3776 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3780 /**********************************************************************
3781 * RemoveMenu (USER32.@)
3783 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3785 LPPOPUPMENU menu;
3786 MENUITEM *item;
3788 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3789 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3790 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3792 /* Remove item */
3794 MENU_FreeItemData( item );
3796 if (--menu->nItems == 0)
3798 HeapFree( GetProcessHeap(), 0, menu->items );
3799 menu->items = NULL;
3801 else
3803 while(nPos < menu->nItems)
3805 *item = *(item+1);
3806 item++;
3807 nPos++;
3809 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3810 menu->nItems * sizeof(MENUITEM) );
3812 return TRUE;
3816 /**********************************************************************
3817 * DeleteMenu (USER32.@)
3819 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3821 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3822 if (!item) return FALSE;
3823 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3824 /* nPos is now the position of the item */
3825 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3826 return TRUE;
3830 /*******************************************************************
3831 * ModifyMenuW (USER32.@)
3833 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3834 UINT_PTR id, LPCWSTR str )
3836 MENUITEM *item;
3838 if (IS_STRING_ITEM(flags))
3839 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3840 else
3841 TRACE("%p %d %04x %04x %p\n", hMenu, pos, flags, id, str );
3843 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3844 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3845 return MENU_SetItemData( item, flags, id, str );
3849 /*******************************************************************
3850 * ModifyMenuA (USER32.@)
3852 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3853 UINT_PTR id, LPCSTR str )
3855 BOOL ret = FALSE;
3857 if (IS_STRING_ITEM(flags) && str)
3859 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3860 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3861 if (newstr)
3863 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3864 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3865 HeapFree( GetProcessHeap(), 0, newstr );
3867 return ret;
3869 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3873 /**********************************************************************
3874 * CreatePopupMenu (USER32.@)
3876 HMENU WINAPI CreatePopupMenu(void)
3878 HMENU hmenu;
3879 POPUPMENU *menu;
3881 if (!(hmenu = CreateMenu())) return 0;
3882 menu = MENU_GetMenu( hmenu );
3883 menu->wFlags |= MF_POPUP;
3884 menu->bTimeToHide = FALSE;
3885 return hmenu;
3889 /**********************************************************************
3890 * GetMenuCheckMarkDimensions (USER.417)
3891 * GetMenuCheckMarkDimensions (USER32.@)
3893 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3895 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3899 /**********************************************************************
3900 * SetMenuItemBitmaps (USER32.@)
3902 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3903 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3905 MENUITEM *item;
3906 TRACE("(%p, %04x, %04x, %p, %p)\n",
3907 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3908 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3910 if (!hNewCheck && !hNewUnCheck)
3912 item->fState &= ~MF_USECHECKBITMAPS;
3914 else /* Install new bitmaps */
3916 item->hCheckBit = hNewCheck;
3917 item->hUnCheckBit = hNewUnCheck;
3918 item->fState |= MF_USECHECKBITMAPS;
3920 return TRUE;
3924 /**********************************************************************
3925 * CreateMenu (USER32.@)
3927 HMENU WINAPI CreateMenu(void)
3929 HMENU hMenu;
3930 LPPOPUPMENU menu;
3931 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3932 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3934 ZeroMemory(menu, sizeof(POPUPMENU));
3935 menu->wMagic = MENU_MAGIC;
3936 menu->FocusedItem = NO_SELECTED_ITEM;
3937 menu->bTimeToHide = FALSE;
3939 TRACE("return %p\n", hMenu );
3941 return hMenu;
3945 /**********************************************************************
3946 * DestroyMenu (USER32.@)
3948 BOOL WINAPI DestroyMenu( HMENU hMenu )
3950 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3952 TRACE("(%p)\n", hMenu);
3955 if (!lppop) return FALSE;
3957 lppop->wMagic = 0; /* Mark it as destroyed */
3959 /* DestroyMenu should not destroy system menu popup owner */
3960 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3962 DestroyWindow( lppop->hWnd );
3963 lppop->hWnd = 0;
3966 if (lppop->items) /* recursively destroy submenus */
3968 int i;
3969 MENUITEM *item = lppop->items;
3970 for (i = lppop->nItems; i > 0; i--, item++)
3972 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3973 MENU_FreeItemData( item );
3975 HeapFree( GetProcessHeap(), 0, lppop->items );
3977 USER_HEAP_FREE( hMenu );
3978 return TRUE;
3982 /**********************************************************************
3983 * GetSystemMenu (USER32.@)
3985 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3987 WND *wndPtr = WIN_GetPtr( hWnd );
3988 HMENU retvalue = 0;
3990 if (wndPtr == WND_DESKTOP) return 0;
3991 if (wndPtr == WND_OTHER_PROCESS)
3993 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3995 else if (wndPtr)
3997 if (wndPtr->hSysMenu && bRevert)
3999 DestroyMenu(wndPtr->hSysMenu);
4000 wndPtr->hSysMenu = 0;
4003 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4004 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4006 if( wndPtr->hSysMenu )
4008 POPUPMENU *menu;
4009 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4011 /* Store the dummy sysmenu handle to facilitate the refresh */
4012 /* of the close button if the SC_CLOSE item change */
4013 menu = MENU_GetMenu(retvalue);
4014 if ( menu )
4015 menu->hSysMenuOwner = wndPtr->hSysMenu;
4017 WIN_ReleasePtr( wndPtr );
4019 return bRevert ? 0 : retvalue;
4023 /*******************************************************************
4024 * SetSystemMenu (USER32.@)
4026 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4028 WND *wndPtr = WIN_GetPtr( hwnd );
4030 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4032 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4033 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4034 WIN_ReleasePtr( wndPtr );
4035 return TRUE;
4037 return FALSE;
4041 /**********************************************************************
4042 * GetMenu (USER32.@)
4044 HMENU WINAPI GetMenu( HWND hWnd )
4046 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4047 TRACE("for %p returning %p\n", hWnd, retvalue);
4048 return retvalue;
4051 /**********************************************************************
4052 * GetMenuBarInfo (USER32.@)
4054 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4056 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4057 return FALSE;
4060 /**********************************************************************
4061 * MENU_SetMenu
4063 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4064 * SetWindowPos call that would result if SetMenu were called directly.
4066 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4068 TRACE("(%p, %p);\n", hWnd, hMenu);
4070 if (hMenu && !IsMenu(hMenu))
4072 WARN("hMenu %p is not a menu handle\n", hMenu);
4073 return FALSE;
4075 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4076 return FALSE;
4078 hWnd = WIN_GetFullHandle( hWnd );
4079 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4081 if (hMenu != 0)
4083 LPPOPUPMENU lpmenu;
4085 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4087 lpmenu->hWnd = hWnd;
4088 lpmenu->Height = 0; /* Make sure we recalculate the size */
4090 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4091 return TRUE;
4095 /**********************************************************************
4096 * SetMenu (USER32.@)
4098 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4100 if(!MENU_SetMenu(hWnd, hMenu))
4101 return FALSE;
4103 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4104 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4105 return TRUE;
4109 /**********************************************************************
4110 * GetSubMenu (USER32.@)
4112 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4114 MENUITEM * lpmi;
4116 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4117 if (!(lpmi->fType & MF_POPUP)) return 0;
4118 return lpmi->hSubMenu;
4122 /**********************************************************************
4123 * DrawMenuBar (USER32.@)
4125 BOOL WINAPI DrawMenuBar( HWND hWnd )
4127 LPPOPUPMENU lppop;
4128 HMENU hMenu = GetMenu(hWnd);
4130 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4131 return FALSE;
4132 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4134 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4135 lppop->hwndOwner = hWnd;
4136 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4137 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4138 return TRUE;
4141 /***********************************************************************
4142 * DrawMenuBarTemp (USER32.@)
4144 * UNDOCUMENTED !!
4146 * called by W98SE desk.cpl Control Panel Applet
4148 * Not 100% sure about the param names, but close.
4150 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4152 LPPOPUPMENU lppop;
4153 UINT i,retvalue;
4154 HFONT hfontOld = 0;
4155 BOOL flat_menu = FALSE;
4157 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4159 if (!hMenu)
4160 hMenu = GetMenu(hwnd);
4162 if (!hFont)
4163 hFont = get_menu_font(FALSE);
4165 lppop = MENU_GetMenu( hMenu );
4166 if (lppop == NULL || lprect == NULL)
4168 retvalue = GetSystemMetrics(SM_CYMENU);
4169 goto END;
4172 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4174 hfontOld = SelectObject( hDC, hFont);
4176 if (lppop->Height == 0)
4177 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4179 lprect->bottom = lprect->top + lppop->Height;
4181 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4183 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4184 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4185 LineTo( hDC, lprect->right, lprect->bottom );
4187 if (lppop->nItems == 0)
4189 retvalue = GetSystemMetrics(SM_CYMENU);
4190 goto END;
4193 for (i = 0; i < lppop->nItems; i++)
4195 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4196 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4198 retvalue = lppop->Height;
4200 END:
4201 if (hfontOld) SelectObject (hDC, hfontOld);
4202 return retvalue;
4205 /***********************************************************************
4206 * EndMenu (USER.187)
4207 * EndMenu (USER32.@)
4209 void WINAPI EndMenu(void)
4211 /* if we are in the menu code, and it is active */
4212 if (!fEndMenu && top_popup)
4214 /* terminate the menu handling code */
4215 fEndMenu = TRUE;
4217 /* needs to be posted to wakeup the internal menu handler */
4218 /* which will now terminate the menu, in the event that */
4219 /* the main window was minimized, or lost focus, so we */
4220 /* don't end up with an orphaned menu */
4221 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4226 /***********************************************************************
4227 * LookupMenuHandle (USER.217)
4229 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4231 HMENU hmenu32 = HMENU_32(hmenu);
4232 UINT id32 = id;
4233 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4234 else return HMENU_16(hmenu32);
4238 /**********************************************************************
4239 * LoadMenu (USER.150)
4241 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4243 HRSRC16 hRsrc;
4244 HGLOBAL16 handle;
4245 HMENU16 hMenu;
4247 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4248 if (!name) return 0;
4250 instance = GetExePtr( instance );
4251 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4252 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4253 hMenu = LoadMenuIndirect16(LockResource16(handle));
4254 FreeResource16( handle );
4255 return hMenu;
4259 /*****************************************************************
4260 * LoadMenuA (USER32.@)
4262 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4264 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4265 if (!hrsrc) return 0;
4266 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4270 /*****************************************************************
4271 * LoadMenuW (USER32.@)
4273 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4275 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4276 if (!hrsrc) return 0;
4277 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4281 /**********************************************************************
4282 * LoadMenuIndirect (USER.220)
4284 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4286 HMENU hMenu;
4287 WORD version, offset;
4288 LPCSTR p = (LPCSTR)template;
4290 TRACE("(%p)\n", template );
4291 version = GET_WORD(p);
4292 p += sizeof(WORD);
4293 if (version)
4295 WARN("version must be 0 for Win16\n" );
4296 return 0;
4298 offset = GET_WORD(p);
4299 p += sizeof(WORD) + offset;
4300 if (!(hMenu = CreateMenu())) return 0;
4301 if (!MENU_ParseResource( p, hMenu, FALSE ))
4303 DestroyMenu( hMenu );
4304 return 0;
4306 return HMENU_16(hMenu);
4310 /**********************************************************************
4311 * LoadMenuIndirectW (USER32.@)
4313 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4315 HMENU hMenu;
4316 WORD version, offset;
4317 LPCSTR p = (LPCSTR)template;
4319 version = GET_WORD(p);
4320 p += sizeof(WORD);
4321 TRACE("%p, ver %d\n", template, version );
4322 switch (version)
4324 case 0: /* standard format is version of 0 */
4325 offset = GET_WORD(p);
4326 p += sizeof(WORD) + offset;
4327 if (!(hMenu = CreateMenu())) return 0;
4328 if (!MENU_ParseResource( p, hMenu, TRUE ))
4330 DestroyMenu( hMenu );
4331 return 0;
4333 return hMenu;
4334 case 1: /* extended format is version of 1 */
4335 offset = GET_WORD(p);
4336 p += sizeof(WORD) + offset;
4337 if (!(hMenu = CreateMenu())) return 0;
4338 if (!MENUEX_ParseResource( p, hMenu))
4340 DestroyMenu( hMenu );
4341 return 0;
4343 return hMenu;
4344 default:
4345 ERR("version %d not supported.\n", version);
4346 return 0;
4351 /**********************************************************************
4352 * LoadMenuIndirectA (USER32.@)
4354 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4356 return LoadMenuIndirectW( template );
4360 /**********************************************************************
4361 * IsMenu (USER32.@)
4363 BOOL WINAPI IsMenu(HMENU hmenu)
4365 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4367 if (!menu)
4369 SetLastError(ERROR_INVALID_MENU_HANDLE);
4370 return FALSE;
4372 return TRUE;
4375 /**********************************************************************
4376 * GetMenuItemInfo_common
4379 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4380 LPMENUITEMINFOW lpmii, BOOL unicode)
4382 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4384 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4386 if (!menu)
4387 return FALSE;
4389 if( lpmii->fMask & MIIM_TYPE) {
4390 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4391 WARN("invalid combination of fMask bits used\n");
4392 /* this does not happen on Win9x/ME */
4393 SetLastError( ERROR_INVALID_PARAMETER);
4394 return FALSE;
4396 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4397 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4398 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4399 if( lpmii->fType & MFT_BITMAP) {
4400 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4401 lpmii->cch = 0;
4402 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4403 /* this does not happen on Win9x/ME */
4404 lpmii->dwTypeData = 0;
4405 lpmii->cch = 0;
4409 /* copy the text string */
4410 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4411 if( !menu->text ) {
4412 if(lpmii->dwTypeData && lpmii->cch) {
4413 lpmii->cch = 0;
4414 if( unicode)
4415 *((WCHAR *)lpmii->dwTypeData) = 0;
4416 else
4417 *((CHAR *)lpmii->dwTypeData) = 0;
4419 } else {
4420 int len;
4421 if (unicode)
4423 len = strlenW(menu->text);
4424 if(lpmii->dwTypeData && lpmii->cch)
4425 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4427 else
4429 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4430 0, NULL, NULL ) - 1;
4431 if(lpmii->dwTypeData && lpmii->cch)
4432 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4433 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4434 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4436 /* if we've copied a substring we return its length */
4437 if(lpmii->dwTypeData && lpmii->cch)
4438 if (lpmii->cch <= len + 1)
4439 lpmii->cch--;
4440 else
4441 lpmii->cch = len;
4442 else {
4443 /* return length of string */
4444 /* not on Win9x/ME if fType & MFT_BITMAP */
4445 lpmii->cch = len;
4450 if (lpmii->fMask & MIIM_FTYPE)
4451 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4453 if (lpmii->fMask & MIIM_BITMAP)
4454 lpmii->hbmpItem = menu->hbmpItem;
4456 if (lpmii->fMask & MIIM_STATE)
4457 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4459 if (lpmii->fMask & MIIM_ID)
4460 lpmii->wID = menu->wID;
4462 if (lpmii->fMask & MIIM_SUBMENU)
4463 lpmii->hSubMenu = menu->hSubMenu;
4464 else {
4465 /* hSubMenu is always cleared
4466 * (not on Win9x/ME ) */
4467 lpmii->hSubMenu = 0;
4470 if (lpmii->fMask & MIIM_CHECKMARKS) {
4471 lpmii->hbmpChecked = menu->hCheckBit;
4472 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4474 if (lpmii->fMask & MIIM_DATA)
4475 lpmii->dwItemData = menu->dwItemData;
4477 return TRUE;
4480 /**********************************************************************
4481 * GetMenuItemInfoA (USER32.@)
4483 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4484 LPMENUITEMINFOA lpmii)
4486 BOOL ret;
4487 MENUITEMINFOA mii;
4488 if( lpmii->cbSize != sizeof( mii) &&
4489 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4490 SetLastError( ERROR_INVALID_PARAMETER);
4491 return FALSE;
4493 memcpy( &mii, lpmii, lpmii->cbSize);
4494 mii.cbSize = sizeof( mii);
4495 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4496 (LPMENUITEMINFOW)&mii, FALSE);
4497 mii.cbSize = lpmii->cbSize;
4498 memcpy( lpmii, &mii, mii.cbSize);
4499 return ret;
4502 /**********************************************************************
4503 * GetMenuItemInfoW (USER32.@)
4505 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4506 LPMENUITEMINFOW lpmii)
4508 BOOL ret;
4509 MENUITEMINFOW mii;
4510 if( lpmii->cbSize != sizeof( mii) &&
4511 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4512 SetLastError( ERROR_INVALID_PARAMETER);
4513 return FALSE;
4515 memcpy( &mii, lpmii, lpmii->cbSize);
4516 mii.cbSize = sizeof( mii);
4517 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4518 mii.cbSize = lpmii->cbSize;
4519 memcpy( lpmii, &mii, mii.cbSize);
4520 return ret;
4524 /* set a menu item text from a ASCII or Unicode string */
4525 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4527 if (!text)
4528 menu->text = NULL;
4529 else if (unicode)
4531 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4532 strcpyW( menu->text, text );
4534 else
4536 LPCSTR str = (LPCSTR)text;
4537 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4538 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4539 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4544 /**********************************************************************
4545 * SetMenuItemInfo_common
4548 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4549 const MENUITEMINFOW *lpmii,
4550 BOOL unicode)
4552 if (!menu) return FALSE;
4554 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4556 if (lpmii->fMask & MIIM_TYPE ) {
4557 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4558 WARN("invalid combination of fMask bits used\n");
4559 /* this does not happen on Win9x/ME */
4560 SetLastError( ERROR_INVALID_PARAMETER);
4561 return FALSE;
4564 /* Remove the old type bits and replace them with the new ones */
4565 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4566 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4568 if (IS_STRING_ITEM(menu->fType)) {
4569 HeapFree(GetProcessHeap(), 0, menu->text);
4570 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4571 } else if( (menu->fType) & MFT_BITMAP)
4572 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4575 if (lpmii->fMask & MIIM_FTYPE ) {
4576 if(( lpmii->fType & MFT_BITMAP)) {
4577 SetLastError( ERROR_INVALID_PARAMETER);
4578 return FALSE;
4580 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4581 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4583 if (lpmii->fMask & MIIM_STRING ) {
4584 /* free the string when used */
4585 HeapFree(GetProcessHeap(), 0, menu->text);
4586 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4589 if (lpmii->fMask & MIIM_STATE)
4591 /* Other menu items having MFS_DEFAULT are not converted
4592 to normal items */
4593 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4596 if (lpmii->fMask & MIIM_ID)
4597 menu->wID = lpmii->wID;
4599 if (lpmii->fMask & MIIM_SUBMENU) {
4600 menu->hSubMenu = lpmii->hSubMenu;
4601 if (menu->hSubMenu) {
4602 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4603 if (subMenu) {
4604 subMenu->wFlags |= MF_POPUP;
4605 menu->fType |= MF_POPUP;
4607 else {
4608 SetLastError( ERROR_INVALID_PARAMETER);
4609 return FALSE;
4612 else
4613 menu->fType &= ~MF_POPUP;
4616 if (lpmii->fMask & MIIM_CHECKMARKS)
4618 menu->hCheckBit = lpmii->hbmpChecked;
4619 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4621 if (lpmii->fMask & MIIM_DATA)
4622 menu->dwItemData = lpmii->dwItemData;
4624 if (lpmii->fMask & MIIM_BITMAP)
4625 menu->hbmpItem = lpmii->hbmpItem;
4627 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4628 menu->fType |= MFT_SEPARATOR;
4630 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4631 return TRUE;
4634 /**********************************************************************
4635 * SetMenuItemInfoA (USER32.@)
4637 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4638 const MENUITEMINFOA *lpmii)
4640 MENUITEMINFOA mii;
4641 if( lpmii->cbSize != sizeof( mii) &&
4642 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4643 SetLastError( ERROR_INVALID_PARAMETER);
4644 return FALSE;
4646 memcpy( &mii, lpmii, lpmii->cbSize);
4647 if( lpmii->cbSize != sizeof( mii)) {
4648 mii.cbSize = sizeof( mii);
4649 mii.hbmpItem = NULL;
4651 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4652 (const MENUITEMINFOW *)&mii, FALSE);
4655 /**********************************************************************
4656 * SetMenuItemInfoW (USER32.@)
4658 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4659 const MENUITEMINFOW *lpmii)
4661 MENUITEMINFOW mii;
4662 if( lpmii->cbSize != sizeof( mii) &&
4663 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4664 SetLastError( ERROR_INVALID_PARAMETER);
4665 return FALSE;
4667 memcpy( &mii, lpmii, lpmii->cbSize);
4668 if( lpmii->cbSize != sizeof( mii)) {
4669 mii.cbSize = sizeof( mii);
4670 mii.hbmpItem = NULL;
4672 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4673 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4676 /**********************************************************************
4677 * SetMenuDefaultItem (USER32.@)
4680 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4682 UINT i;
4683 POPUPMENU *menu;
4684 MENUITEM *item;
4686 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4688 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4690 /* reset all default-item flags */
4691 item = menu->items;
4692 for (i = 0; i < menu->nItems; i++, item++)
4694 item->fState &= ~MFS_DEFAULT;
4697 /* no default item */
4698 if ( -1 == uItem)
4700 return TRUE;
4703 item = menu->items;
4704 if ( bypos )
4706 if ( uItem >= menu->nItems ) return FALSE;
4707 item[uItem].fState |= MFS_DEFAULT;
4708 return TRUE;
4710 else
4712 for (i = 0; i < menu->nItems; i++, item++)
4714 if (item->wID == uItem)
4716 item->fState |= MFS_DEFAULT;
4717 return TRUE;
4722 return FALSE;
4725 /**********************************************************************
4726 * GetMenuDefaultItem (USER32.@)
4728 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4730 POPUPMENU *menu;
4731 MENUITEM * item;
4732 UINT i = 0;
4734 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4736 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4738 /* find default item */
4739 item = menu->items;
4741 /* empty menu */
4742 if (! item) return -1;
4744 while ( !( item->fState & MFS_DEFAULT ) )
4746 i++; item++;
4747 if (i >= menu->nItems ) return -1;
4750 /* default: don't return disabled items */
4751 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4753 /* search rekursiv when needed */
4754 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4756 UINT ret;
4757 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4758 if ( -1 != ret ) return ret;
4760 /* when item not found in submenu, return the popup item */
4762 return ( bypos ) ? i : item->wID;
4767 /**********************************************************************
4768 * InsertMenuItemA (USER32.@)
4770 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4771 const MENUITEMINFOA *lpmii)
4773 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4774 MENUITEMINFOA mii;
4775 if( lpmii->cbSize != sizeof( mii) &&
4776 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4777 SetLastError( ERROR_INVALID_PARAMETER);
4778 return FALSE;
4780 memcpy( &mii, lpmii, lpmii->cbSize);
4781 if( lpmii->cbSize != sizeof( mii)) {
4782 mii.cbSize = sizeof( mii);
4783 mii.hbmpItem = NULL;
4785 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4789 /**********************************************************************
4790 * InsertMenuItemW (USER32.@)
4792 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4793 const MENUITEMINFOW *lpmii)
4795 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4796 MENUITEMINFOW mii;
4797 if( lpmii->cbSize != sizeof( mii) &&
4798 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4799 SetLastError( ERROR_INVALID_PARAMETER);
4800 return FALSE;
4802 memcpy( &mii, lpmii, lpmii->cbSize);
4803 if( lpmii->cbSize != sizeof( mii)) {
4804 mii.cbSize = sizeof( mii);
4805 mii.hbmpItem = NULL;
4807 return SetMenuItemInfo_common(item, &mii, TRUE);
4810 /**********************************************************************
4811 * CheckMenuRadioItem (USER32.@)
4814 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4815 UINT first, UINT last, UINT check,
4816 UINT bypos)
4818 MENUITEM *mifirst, *milast, *micheck;
4819 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4821 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4823 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4824 milast = MENU_FindItem (&mlast, &last, bypos);
4825 micheck = MENU_FindItem (&mcheck, &check, bypos);
4827 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4828 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4829 micheck > milast || micheck < mifirst)
4830 return FALSE;
4832 while (mifirst <= milast)
4834 if (mifirst == micheck)
4836 mifirst->fType |= MFT_RADIOCHECK;
4837 mifirst->fState |= MFS_CHECKED;
4838 } else {
4839 mifirst->fType &= ~MFT_RADIOCHECK;
4840 mifirst->fState &= ~MFS_CHECKED;
4842 mifirst++;
4845 return TRUE;
4849 /**********************************************************************
4850 * GetMenuItemRect (USER32.@)
4852 * ATTENTION: Here, the returned values in rect are the screen
4853 * coordinates of the item just like if the menu was
4854 * always on the upper left side of the application.
4857 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4858 LPRECT rect)
4860 POPUPMENU *itemMenu;
4861 MENUITEM *item;
4862 HWND referenceHwnd;
4864 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4866 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4867 referenceHwnd = hwnd;
4869 if(!hwnd)
4871 itemMenu = MENU_GetMenu(hMenu);
4872 if (itemMenu == NULL)
4873 return FALSE;
4875 if(itemMenu->hWnd == 0)
4876 return FALSE;
4877 referenceHwnd = itemMenu->hWnd;
4880 if ((rect == NULL) || (item == NULL))
4881 return FALSE;
4883 *rect = item->rect;
4885 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4887 return TRUE;
4891 /**********************************************************************
4892 * SetMenuInfo (USER32.@)
4894 * FIXME
4895 * MIM_APPLYTOSUBMENUS
4896 * actually use the items to draw the menu
4898 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4900 POPUPMENU *menu;
4902 TRACE("(%p %p)\n", hMenu, lpmi);
4904 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4907 if (lpmi->fMask & MIM_BACKGROUND)
4908 menu->hbrBack = lpmi->hbrBack;
4910 if (lpmi->fMask & MIM_HELPID)
4911 menu->dwContextHelpID = lpmi->dwContextHelpID;
4913 if (lpmi->fMask & MIM_MAXHEIGHT)
4914 menu->cyMax = lpmi->cyMax;
4916 if (lpmi->fMask & MIM_MENUDATA)
4917 menu->dwMenuData = lpmi->dwMenuData;
4919 if (lpmi->fMask & MIM_STYLE)
4921 menu->dwStyle = lpmi->dwStyle;
4922 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4923 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4924 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4925 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
4928 return TRUE;
4930 return FALSE;
4933 /**********************************************************************
4934 * GetMenuInfo (USER32.@)
4936 * NOTES
4937 * win98/NT5.0
4940 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4941 { POPUPMENU *menu;
4943 TRACE("(%p %p)\n", hMenu, lpmi);
4945 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4948 if (lpmi->fMask & MIM_BACKGROUND)
4949 lpmi->hbrBack = menu->hbrBack;
4951 if (lpmi->fMask & MIM_HELPID)
4952 lpmi->dwContextHelpID = menu->dwContextHelpID;
4954 if (lpmi->fMask & MIM_MAXHEIGHT)
4955 lpmi->cyMax = menu->cyMax;
4957 if (lpmi->fMask & MIM_MENUDATA)
4958 lpmi->dwMenuData = menu->dwMenuData;
4960 if (lpmi->fMask & MIM_STYLE)
4961 lpmi->dwStyle = menu->dwStyle;
4963 return TRUE;
4965 return FALSE;
4969 /**********************************************************************
4970 * SetMenuContextHelpId (USER32.@)
4972 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4974 LPPOPUPMENU menu;
4976 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
4978 if ((menu = MENU_GetMenu(hMenu)))
4980 menu->dwContextHelpID = dwContextHelpID;
4981 return TRUE;
4983 return FALSE;
4987 /**********************************************************************
4988 * GetMenuContextHelpId (USER32.@)
4990 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4992 LPPOPUPMENU menu;
4994 TRACE("(%p)\n", hMenu);
4996 if ((menu = MENU_GetMenu(hMenu)))
4998 return menu->dwContextHelpID;
5000 return 0;
5003 /**********************************************************************
5004 * MenuItemFromPoint (USER32.@)
5006 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5008 POPUPMENU *menu = MENU_GetMenu(hMenu);
5009 UINT pos;
5011 /*FIXME: Do we have to handle hWnd here? */
5012 if (!menu) return -1;
5013 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5014 return pos;
5018 /**********************************************************************
5019 * translate_accelerator
5021 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5022 BYTE fVirt, WORD key, WORD cmd )
5024 INT mask = 0;
5025 UINT mesg = 0;
5027 if (wParam != key) return FALSE;
5029 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5030 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5031 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5033 if (message == WM_CHAR || message == WM_SYSCHAR)
5035 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5037 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
5038 goto found;
5041 else
5043 if(fVirt & FVIRTKEY)
5045 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5046 wParam, 0xff & HIWORD(lParam));
5048 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5049 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5051 else
5053 if (!(lParam & 0x01000000)) /* no special_key */
5055 if ((fVirt & FALT) && (lParam & 0x20000000))
5056 { /* ^^ ALT pressed */
5057 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5058 goto found;
5063 return FALSE;
5065 found:
5066 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5067 mesg = 1;
5068 else
5070 HMENU hMenu, hSubMenu, hSysMenu;
5071 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5073 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5074 hSysMenu = get_win_sys_menu( hWnd );
5076 /* find menu item and ask application to initialize it */
5077 /* 1. in the system menu */
5078 hSubMenu = hSysMenu;
5079 nPos = cmd;
5080 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5082 if (GetCapture())
5083 mesg = 2;
5084 if (!IsWindowEnabled(hWnd))
5085 mesg = 3;
5086 else
5088 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5089 if(hSubMenu != hSysMenu)
5091 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5092 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5093 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5095 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5098 else /* 2. in the window's menu */
5100 hSubMenu = hMenu;
5101 nPos = cmd;
5102 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5104 if (GetCapture())
5105 mesg = 2;
5106 if (!IsWindowEnabled(hWnd))
5107 mesg = 3;
5108 else
5110 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5111 if(hSubMenu != hMenu)
5113 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5114 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5115 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5117 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5122 if (mesg == 0)
5124 if (uSysStat != (UINT)-1)
5126 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5127 mesg=4;
5128 else
5129 mesg=WM_SYSCOMMAND;
5131 else
5133 if (uStat != (UINT)-1)
5135 if (IsIconic(hWnd))
5136 mesg=5;
5137 else
5139 if (uStat & (MF_DISABLED|MF_GRAYED))
5140 mesg=6;
5141 else
5142 mesg=WM_COMMAND;
5145 else
5146 mesg=WM_COMMAND;
5151 if( mesg==WM_COMMAND )
5153 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5154 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5156 else if( mesg==WM_SYSCOMMAND )
5158 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5159 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5161 else
5163 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5164 * #0: unknown (please report!)
5165 * #1: for WM_KEYUP,WM_SYSKEYUP
5166 * #2: mouse is captured
5167 * #3: window is disabled
5168 * #4: it's a disabled system menu option
5169 * #5: it's a menu option, but window is iconic
5170 * #6: it's a menu option, but disabled
5172 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5173 if(mesg==0)
5174 ERR_(accel)(" unknown reason - please report!\n");
5176 return TRUE;
5179 /**********************************************************************
5180 * TranslateAcceleratorA (USER32.@)
5181 * TranslateAccelerator (USER32.@)
5183 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5185 /* YES, Accel16! */
5186 LPACCEL16 lpAccelTbl;
5187 int i;
5188 WPARAM wParam;
5190 if (!hWnd || !msg) return 0;
5192 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5194 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5195 return 0;
5198 wParam = msg->wParam;
5200 switch (msg->message)
5202 case WM_KEYDOWN:
5203 case WM_SYSKEYDOWN:
5204 break;
5206 case WM_CHAR:
5207 case WM_SYSCHAR:
5209 char ch = LOWORD(wParam);
5210 WCHAR wch;
5211 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5212 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5214 break;
5216 default:
5217 return 0;
5220 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5221 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5222 i = 0;
5225 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5226 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5227 return 1;
5228 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5230 return 0;
5233 /**********************************************************************
5234 * TranslateAcceleratorW (USER32.@)
5236 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5238 /* YES, Accel16! */
5239 LPACCEL16 lpAccelTbl;
5240 int i;
5242 if (!hWnd || !msg) return 0;
5244 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5246 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5247 return 0;
5250 switch (msg->message)
5252 case WM_KEYDOWN:
5253 case WM_SYSKEYDOWN:
5254 case WM_CHAR:
5255 case WM_SYSCHAR:
5256 break;
5258 default:
5259 return 0;
5262 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5263 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5264 i = 0;
5267 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5268 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5269 return 1;
5270 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5272 return 0;