user32: Skip system menu entries when using keyboard.
[wine.git] / dlls / user32 / menu.c
blob2f57779a30557d10cfd6a95e7a51a53f00a6fb84
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
56 #include "wownt32.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "win.h"
60 #include "controls.h"
61 #include "user_private.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(menu);
65 WINE_DECLARE_DEBUG_CHANNEL(accel);
67 /* internal popup menu window messages */
69 #define MM_SETMENUHANDLE (WM_USER + 0)
70 #define MM_GETMENUHANDLE (WM_USER + 1)
72 /* Menu item structure */
73 typedef struct {
74 /* ----------- MENUITEMINFO Stuff ----------- */
75 UINT fType; /* Item type. */
76 UINT fState; /* Item state. */
77 UINT_PTR wID; /* Item id. */
78 HMENU hSubMenu; /* Pop-up menu. */
79 HBITMAP hCheckBit; /* Bitmap when checked. */
80 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
81 LPWSTR text; /* Item text. */
82 ULONG_PTR dwItemData; /* Application defined. */
83 LPWSTR dwTypeData; /* depends on fMask */
84 HBITMAP hbmpItem; /* bitmap */
85 /* ----------- Wine stuff ----------- */
86 RECT rect; /* Item area (relative to menu window) */
87 UINT xTab; /* X position of text after Tab */
88 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
89 * bitmap */
90 } MENUITEM;
92 /* Popup menu structure */
93 typedef struct {
94 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
95 WORD wMagic; /* Magic number */
96 WORD Width; /* Width of the whole menu */
97 WORD Height; /* Height of the whole menu */
98 UINT nItems; /* Number of items in the menu */
99 HWND hWnd; /* Window containing the menu */
100 MENUITEM *items; /* Array of menu items */
101 UINT FocusedItem; /* Currently focused item */
102 HWND hwndOwner; /* window receiving the messages for ownerdraw */
103 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
104 BOOL bScrolling; /* Scroll arrows are active */
105 UINT nScrollPos; /* Current scroll position */
106 UINT nTotalHeight; /* Total height of menu items inside menu */
107 /* ------------ MENUINFO members ------ */
108 DWORD dwStyle; /* Extended menu style */
109 UINT cyMax; /* max height of the whole menu, 0 is screen height */
110 HBRUSH hbrBack; /* brush for menu background */
111 DWORD dwContextHelpID;
112 DWORD dwMenuData; /* application defined value */
113 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
114 SIZE maxBmpSize; /* Maximum size of the bitmap items */
115 } POPUPMENU, *LPPOPUPMENU;
117 /* internal flags for menu tracking */
119 #define TF_ENDMENU 0x0001
120 #define TF_SUSPENDPOPUP 0x0002
121 #define TF_SKIPREMOVE 0x0004
123 typedef struct
125 UINT trackFlags;
126 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
127 HMENU hTopMenu; /* initial menu */
128 HWND hOwnerWnd; /* where notifications are sent */
129 POINT pt;
130 } MTRACKER;
132 #define MENU_MAGIC 0x554d /* 'MU' */
134 #define ITEM_PREV -1
135 #define ITEM_NEXT 1
137 /* Internal MENU_TrackMenu() flags */
138 #define TPM_INTERNAL 0xF0000000
139 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
140 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
141 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
143 /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
146 /* top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
150 /* (other menu->FocusedItem values give the position of the focused item) */
151 #define NO_SELECTED_ITEM 0xffff
153 #define MENU_ITEM_TYPE(flags) \
154 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
156 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
157 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
158 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
160 #define IS_SYSTEM_MENU(menu) \
161 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
163 #define MENUITEMINFO_TYPE_MASK \
164 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
165 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
166 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
167 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
168 #define STATE_MASK (~TYPE_MASK)
169 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
171 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
173 static SIZE menucharsize;
174 static UINT ODitemheight; /* default owner drawn item height */
176 /* Use global popup window because there's no way 2 menus can
177 * be tracked at the same time. */
178 static HWND top_popup;
180 /* Flag set by EndMenu() to force an exit from menu tracking */
181 static BOOL fEndMenu = FALSE;
183 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
185 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
187 /*********************************************************************
188 * menu class descriptor
190 const struct builtin_class_descr MENU_builtin_class =
192 (LPCSTR)POPUPMENU_CLASS_ATOM, /* name */
193 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
194 NULL, /* procA (winproc is Unicode only) */
195 PopupMenuWndProc, /* procW */
196 sizeof(HMENU), /* extra */
197 IDC_ARROW, /* cursor */
198 (HBRUSH)(COLOR_MENU+1) /* brush */
202 /***********************************************************************
203 * debug_print_menuitem
205 * Print a menuitem in readable form.
208 #define debug_print_menuitem(pre, mp, post) \
209 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
211 #define MENUOUT(text) \
212 TRACE("%s%s", (count++ ? "," : ""), (text))
214 #define MENUFLAG(bit,text) \
215 do { \
216 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
217 } while (0)
219 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
220 const char *postfix)
222 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
223 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
224 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
225 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
226 TRACE("%s ", prefix);
227 if (mp) {
228 UINT flags = mp->fType;
229 TRACE( "{ ID=0x%lx", mp->wID);
230 if ( mp->hSubMenu)
231 TRACE( ", Sub=%p", mp->hSubMenu);
232 if (flags) {
233 int count = 0;
234 TRACE( ", fType=");
235 MENUFLAG( MFT_SEPARATOR, "sep");
236 MENUFLAG( MFT_OWNERDRAW, "own");
237 MENUFLAG( MFT_BITMAP, "bit");
238 MENUFLAG(MF_POPUP, "pop");
239 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240 MENUFLAG(MFT_MENUBREAK, "brk");
241 MENUFLAG(MFT_RADIOCHECK, "radio");
242 MENUFLAG(MFT_RIGHTORDER, "rorder");
243 MENUFLAG(MF_SYSMENU, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
245 if (flags)
246 TRACE( "+0x%x", flags);
248 flags = mp->fState;
249 if (flags) {
250 int count = 0;
251 TRACE( ", State=");
252 MENUFLAG(MFS_GRAYED, "grey");
253 MENUFLAG(MFS_DEFAULT, "default");
254 MENUFLAG(MFS_DISABLED, "dis");
255 MENUFLAG(MFS_CHECKED, "check");
256 MENUFLAG(MFS_HILITE, "hi");
257 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
258 MENUFLAG(MF_MOUSESELECT, "mouse");
259 if (flags)
260 TRACE( "+0x%x", flags);
262 if (mp->hCheckBit)
263 TRACE( ", Chk=%p", mp->hCheckBit);
264 if (mp->hUnCheckBit)
265 TRACE( ", Unc=%p", mp->hUnCheckBit);
266 if (mp->text)
267 TRACE( ", Text=%s", debugstr_w(mp->text));
268 if (mp->dwItemData)
269 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
270 if (mp->hbmpItem)
272 if( IS_MAGIC_BITMAP(mp->hbmpItem))
273 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
274 else
275 TRACE( ", hbitmap=%p", mp->hbmpItem);
277 TRACE( " }");
278 } else
279 TRACE( "NULL");
280 TRACE(" %s\n", postfix);
283 #undef MENUOUT
284 #undef MENUFLAG
287 /***********************************************************************
288 * MENU_GetMenu
290 * Validate the given menu handle and returns the menu structure pointer.
292 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
294 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
295 if (!menu || menu->wMagic != MENU_MAGIC)
297 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
298 menu = NULL;
300 return menu;
303 /***********************************************************************
304 * get_win_sys_menu
306 * Get the system menu of a window
308 static HMENU get_win_sys_menu( HWND hwnd )
310 HMENU ret = 0;
311 WND *win = WIN_GetPtr( hwnd );
312 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
314 ret = win->hSysMenu;
315 WIN_ReleasePtr( win );
317 return ret;
320 /***********************************************************************
321 * get_menu_font
323 static HFONT get_menu_font( BOOL bold )
325 static HFONT hMenuFont, hMenuFontBold;
327 HFONT ret = bold ? hMenuFontBold : hMenuFont;
329 if (!ret)
331 NONCLIENTMETRICSW ncm;
332 HFONT prev;
334 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
335 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
337 if (bold)
339 ncm.lfMenuFont.lfWeight += 300;
340 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
342 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
343 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
344 ret, NULL );
345 if (prev)
347 /* another thread beat us to it */
348 DeleteObject( ret );
349 ret = prev;
352 return ret;
355 /***********************************************************************
356 * get_arrow_bitmap
358 static HBITMAP get_arrow_bitmap(void)
360 static HBITMAP arrow_bitmap;
362 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
363 return arrow_bitmap;
366 /***********************************************************************
367 * get_down_arrow_bitmap
369 static HBITMAP get_down_arrow_bitmap(void)
371 static HBITMAP arrow_bitmap;
373 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
374 return arrow_bitmap;
377 /***********************************************************************
378 * get_down_arrow_inactive_bitmap
380 static HBITMAP get_down_arrow_inactive_bitmap(void)
382 static HBITMAP arrow_bitmap;
384 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
385 return arrow_bitmap;
388 /***********************************************************************
389 * get_up_arrow_bitmap
391 static HBITMAP get_up_arrow_bitmap(void)
393 static HBITMAP arrow_bitmap;
395 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
396 return arrow_bitmap;
399 /***********************************************************************
400 * get_up_arrow_inactive_bitmap
402 static HBITMAP get_up_arrow_inactive_bitmap(void)
404 static HBITMAP arrow_bitmap;
406 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
407 return arrow_bitmap;
410 /***********************************************************************
411 * MENU_CopySysPopup
413 * Return the default system menu.
415 static HMENU MENU_CopySysPopup(void)
417 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
418 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
420 if( hMenu ) {
421 POPUPMENU* menu = MENU_GetMenu(hMenu);
422 menu->wFlags |= MF_SYSMENU | MF_POPUP;
423 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
425 else
426 ERR("Unable to load default system menu\n" );
428 TRACE("returning %p.\n", hMenu );
430 return hMenu;
434 /**********************************************************************
435 * MENU_GetSysMenu
437 * Create a copy of the system menu. System menu in Windows is
438 * a special menu bar with the single entry - system menu popup.
439 * This popup is presented to the outside world as a "system menu".
440 * However, the real system menu handle is sometimes seen in the
441 * WM_MENUSELECT parameters (and Word 6 likes it this way).
443 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
445 HMENU hMenu;
447 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
448 if ((hMenu = CreateMenu()))
450 POPUPMENU *menu = MENU_GetMenu(hMenu);
451 menu->wFlags = MF_SYSMENU;
452 menu->hWnd = WIN_GetFullHandle( hWnd );
453 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
455 if (!hPopupMenu)
456 hPopupMenu = MENU_CopySysPopup();
458 if (hPopupMenu)
460 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
461 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
463 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
464 (UINT_PTR)hPopupMenu, NULL );
466 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
467 menu->items[0].fState = 0;
468 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
470 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
471 return hMenu;
473 DestroyMenu( hMenu );
475 ERR("failed to load system menu!\n");
476 return 0;
480 /***********************************************************************
481 * MENU_InitSysMenuPopup
483 * Grey the appropriate items in System menu.
485 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
487 BOOL gray;
489 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
490 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
491 gray = ((style & WS_MAXIMIZE) != 0);
492 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
493 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
494 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
496 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
497 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
498 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
499 gray = (clsStyle & CS_NOCLOSE) != 0;
501 /* The menu item must keep its state if it's disabled */
502 if(gray)
503 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
507 /******************************************************************************
509 * UINT MENU_GetStartOfNextColumn(
510 * HMENU hMenu )
512 *****************************************************************************/
514 static UINT MENU_GetStartOfNextColumn(
515 HMENU hMenu )
517 POPUPMENU *menu = MENU_GetMenu(hMenu);
518 UINT i;
520 if(!menu)
521 return NO_SELECTED_ITEM;
523 i = menu->FocusedItem + 1;
524 if( i == NO_SELECTED_ITEM )
525 return i;
527 for( ; i < menu->nItems; ++i ) {
528 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
529 return i;
532 return NO_SELECTED_ITEM;
536 /******************************************************************************
538 * UINT MENU_GetStartOfPrevColumn(
539 * HMENU hMenu )
541 *****************************************************************************/
543 static UINT MENU_GetStartOfPrevColumn(
544 HMENU hMenu )
546 POPUPMENU *menu = MENU_GetMenu(hMenu);
547 UINT i;
549 if( !menu )
550 return NO_SELECTED_ITEM;
552 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
553 return NO_SELECTED_ITEM;
555 /* Find the start of the column */
557 for(i = menu->FocusedItem; i != 0 &&
558 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
559 --i); /* empty */
561 if(i == 0)
562 return NO_SELECTED_ITEM;
564 for(--i; i != 0; --i) {
565 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
566 break;
569 TRACE("ret %d.\n", i );
571 return i;
576 /***********************************************************************
577 * MENU_FindItem
579 * Find a menu item. Return a pointer on the item, and modifies *hmenu
580 * in case the item was in a sub-menu.
582 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
584 POPUPMENU *menu;
585 MENUITEM *fallback = NULL;
586 UINT fallback_pos = 0;
587 UINT i;
589 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
590 if (wFlags & MF_BYPOSITION)
592 if (*nPos >= menu->nItems) return NULL;
593 return &menu->items[*nPos];
595 else
597 MENUITEM *item = menu->items;
598 for (i = 0; i < menu->nItems; i++, item++)
600 if (item->fType & MF_POPUP)
602 HMENU hsubmenu = item->hSubMenu;
603 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
604 if (subitem)
606 *hmenu = hsubmenu;
607 return subitem;
609 else if (item->wID == *nPos)
611 /* fallback to this item if nothing else found */
612 fallback_pos = i;
613 fallback = item;
616 else if (item->wID == *nPos)
618 *nPos = i;
619 return item;
624 if (fallback)
625 *nPos = fallback_pos;
627 return fallback;
630 /***********************************************************************
631 * MENU_FindSubMenu
633 * Find a Sub menu. Return the position of the submenu, and modifies
634 * *hmenu in case it is found in another sub-menu.
635 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
637 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
639 POPUPMENU *menu;
640 UINT i;
641 MENUITEM *item;
642 if (((*hmenu)==(HMENU)0xffff) ||
643 (!(menu = MENU_GetMenu(*hmenu))))
644 return NO_SELECTED_ITEM;
645 item = menu->items;
646 for (i = 0; i < menu->nItems; i++, item++) {
647 if(!(item->fType & MF_POPUP)) continue;
648 if (item->hSubMenu == hSubTarget) {
649 return i;
651 else {
652 HMENU hsubmenu = item->hSubMenu;
653 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
654 if (pos != NO_SELECTED_ITEM) {
655 *hmenu = hsubmenu;
656 return pos;
660 return NO_SELECTED_ITEM;
663 /***********************************************************************
664 * MENU_FreeItemData
666 static void MENU_FreeItemData( MENUITEM* item )
668 /* delete text */
669 HeapFree( GetProcessHeap(), 0, item->text );
672 /***********************************************************************
673 * MENU_AdjustMenuItemRect
675 * Adjust menu item rectangle according to scrolling state.
677 static void
678 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
680 if (menu->bScrolling)
682 UINT arrow_bitmap_width, arrow_bitmap_height;
683 BITMAP bmp;
685 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
686 arrow_bitmap_width = bmp.bmWidth;
687 arrow_bitmap_height = bmp.bmHeight;
688 rect->top += arrow_bitmap_height - menu->nScrollPos;
689 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
694 /***********************************************************************
695 * MENU_FindItemByCoords
697 * Find the item at the specified coordinates (screen coords). Does
698 * not work for child windows and therefore should not be called for
699 * an arbitrary system menu.
701 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
702 POINT pt, UINT *pos )
704 MENUITEM *item;
705 UINT i;
706 RECT wrect;
707 RECT rect;
709 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
710 pt.x -= wrect.left;pt.y -= wrect.top;
711 item = menu->items;
712 for (i = 0; i < menu->nItems; i++, item++)
714 rect = item->rect;
715 MENU_AdjustMenuItemRect(menu, &rect);
716 if ((pt.x >= rect.left) && (pt.x < rect.right) &&
717 (pt.y >= rect.top) && (pt.y < rect.bottom))
719 if (pos) *pos = i;
720 return item;
723 return NULL;
727 /***********************************************************************
728 * MENU_FindItemByKey
730 * Find the menu item selected by a key press.
731 * Return item id, -1 if none, -2 if we should close the menu.
733 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
734 WCHAR key, BOOL forceMenuChar )
736 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
738 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
740 if (hmenu)
742 POPUPMENU *menu = MENU_GetMenu( hmenu );
743 MENUITEM *item = menu->items;
744 LRESULT menuchar;
746 if( !forceMenuChar )
748 UINT i;
750 for (i = 0; i < menu->nItems; i++, item++)
752 if( item->text)
754 WCHAR *p = item->text - 2;
757 p = strchrW (p + 2, '&');
759 while (p != NULL && p [1] == '&');
760 if (p && (toupperW(p[1]) == toupperW(key))) return i;
764 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
765 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
766 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
767 if (HIWORD(menuchar) == 1) return (UINT)(-2);
769 return (UINT)(-1);
773 /***********************************************************************
774 * MENU_GetBitmapItemSize
776 * Get the size of a bitmap item.
778 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
779 HWND hwndOwner)
781 BITMAP bm;
782 HBITMAP bmp = lpitem->hbmpItem;
784 size->cx = size->cy = 0;
786 /* check if there is a magic menu item associated with this item */
787 switch( (INT_PTR) bmp )
789 case (INT_PTR)HBMMENU_CALLBACK:
791 MEASUREITEMSTRUCT measItem;
792 measItem.CtlType = ODT_MENU;
793 measItem.CtlID = 0;
794 measItem.itemID = lpitem->wID;
795 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
796 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
797 measItem.itemData = lpitem->dwItemData;
798 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
799 size->cx = measItem.itemWidth;
800 size->cy = measItem.itemHeight;
801 return;
803 break;
804 case (INT_PTR)HBMMENU_SYSTEM:
805 if (lpitem->dwItemData)
807 bmp = (HBITMAP)lpitem->dwItemData;
808 break;
810 /* fall through */
811 case (INT_PTR)HBMMENU_MBAR_RESTORE:
812 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
813 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
814 case (INT_PTR)HBMMENU_MBAR_CLOSE:
815 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
816 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
817 size->cy = size->cx;
818 return;
819 case (INT_PTR)HBMMENU_POPUP_CLOSE:
820 case (INT_PTR)HBMMENU_POPUP_RESTORE:
821 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
822 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
823 FIXME("Magic %p not implemented\n", bmp );
824 return;
826 if (GetObjectW(bmp, sizeof(bm), &bm ))
828 size->cx = bm.bmWidth;
829 size->cy = bm.bmHeight;
833 /***********************************************************************
834 * MENU_DrawBitmapItem
836 * Draw a bitmap item.
838 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
839 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
841 BITMAP bm;
842 DWORD rop;
843 HDC hdcMem;
844 HBITMAP bmp;
845 int w = rect->right - rect->left;
846 int h = rect->bottom - rect->top;
847 int bmp_xoffset = 0;
848 int left, top;
849 HBITMAP hbmToDraw = lpitem->hbmpItem;
850 bmp = hbmToDraw;
852 /* Check if there is a magic menu item associated with this item */
853 if (IS_MAGIC_BITMAP(hbmToDraw))
855 UINT flags = 0;
856 RECT r;
858 switch((INT_PTR)hbmToDraw)
860 case (INT_PTR)HBMMENU_SYSTEM:
861 if (lpitem->dwItemData)
863 bmp = (HBITMAP)lpitem->dwItemData;
864 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
866 else
868 static HBITMAP hBmpSysMenu;
870 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
871 bmp = hBmpSysMenu;
872 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
873 /* only use right half of the bitmap */
874 bmp_xoffset = bm.bmWidth / 2;
875 bm.bmWidth -= bmp_xoffset;
877 goto got_bitmap;
878 case (INT_PTR)HBMMENU_MBAR_RESTORE:
879 flags = DFCS_CAPTIONRESTORE;
880 break;
881 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
882 flags = DFCS_CAPTIONMIN;
883 break;
884 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
885 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
886 break;
887 case (INT_PTR)HBMMENU_MBAR_CLOSE:
888 flags = DFCS_CAPTIONCLOSE;
889 break;
890 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
891 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
892 break;
893 case (INT_PTR)HBMMENU_CALLBACK:
895 DRAWITEMSTRUCT drawItem;
896 drawItem.CtlType = ODT_MENU;
897 drawItem.CtlID = 0;
898 drawItem.itemID = lpitem->wID;
899 drawItem.itemAction = odaction;
900 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
901 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
902 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
903 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
904 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
905 drawItem.hwndItem = (HWND)hmenu;
906 drawItem.hDC = hdc;
907 drawItem.itemData = lpitem->dwItemData;
908 drawItem.rcItem = *rect;
909 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
910 return;
912 break;
913 case (INT_PTR)HBMMENU_POPUP_CLOSE:
914 case (INT_PTR)HBMMENU_POPUP_RESTORE:
915 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
916 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
917 default:
918 FIXME("Magic %p not implemented\n", hbmToDraw);
919 return;
921 r = *rect;
922 InflateRect( &r, -1, -1 );
923 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
924 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
925 return;
928 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
930 got_bitmap:
931 hdcMem = CreateCompatibleDC( hdc );
932 SelectObject( hdcMem, bmp );
934 /* handle fontsize > bitmap_height */
935 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
936 left=rect->left;
937 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
938 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
939 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
940 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
941 DeleteDC( hdcMem );
945 /***********************************************************************
946 * MENU_CalcItemSize
948 * Calculate the size of the menu item and store it in lpitem->rect.
950 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
951 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
953 WCHAR *p;
954 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
955 UINT arrow_bitmap_width;
956 BITMAP bm;
957 INT itemheight;
959 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
960 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
961 (menuBar ? " (MenuBar)" : ""));
963 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
964 arrow_bitmap_width = bm.bmWidth;
966 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
967 if( !menucharsize.cx ) {
968 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
969 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
970 * but it is unlikely an application will depend on that */
971 ODitemheight = HIWORD( GetDialogBaseUnits());
974 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
976 if (lpitem->fType & MF_OWNERDRAW)
978 MEASUREITEMSTRUCT mis;
979 mis.CtlType = ODT_MENU;
980 mis.CtlID = 0;
981 mis.itemID = lpitem->wID;
982 mis.itemData = lpitem->dwItemData;
983 mis.itemHeight = ODitemheight;
984 mis.itemWidth = 0;
985 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
986 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
987 * width of a menufont character to the width of an owner-drawn menu.
989 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
990 if (menuBar) {
991 /* under at least win95 you seem to be given a standard
992 height for the menu and the height value is ignored */
993 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
994 } else
995 lpitem->rect.bottom += mis.itemHeight;
997 TRACE("id=%04lx size=%dx%d\n",
998 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
999 lpitem->rect.bottom-lpitem->rect.top);
1000 return;
1003 if (lpitem->fType & MF_SEPARATOR)
1005 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1006 if( !menuBar)
1007 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1008 return;
1011 itemheight = 0;
1012 lpitem->xTab = 0;
1014 if (!menuBar) {
1015 if (lpitem->hbmpItem) {
1016 SIZE size;
1018 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1019 /* Keep the size of the bitmap in callback mode to be able
1020 * to draw it correctly */
1021 lpitem->bmpsize = size;
1022 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1023 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1024 lpitem->rect.right += size.cx + 2;
1025 itemheight = size.cy + 2;
1027 if( !(lppop->dwStyle & MNS_NOCHECK))
1028 lpitem->rect.right += check_bitmap_width;
1029 lpitem->rect.right += 4 + menucharsize.cx;
1030 lpitem->xTab = lpitem->rect.right;
1031 lpitem->rect.right += arrow_bitmap_width;
1032 } else if (lpitem->hbmpItem) { /* menuBar */
1033 SIZE size;
1035 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1036 lpitem->bmpsize = size;
1037 lpitem->rect.right += size.cx;
1038 if( lpitem->text) lpitem->rect.right += 2;
1039 itemheight = size.cy;
1042 /* it must be a text item - unless it's the system menu */
1043 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1044 HFONT hfontOld = NULL;
1045 RECT rc = lpitem->rect;
1046 LONG txtheight, txtwidth;
1048 if ( lpitem->fState & MFS_DEFAULT ) {
1049 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1051 if (menuBar) {
1052 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1053 DT_SINGLELINE|DT_CALCRECT);
1054 lpitem->rect.right += rc.right - rc.left;
1055 itemheight = max( max( itemheight, txtheight),
1056 GetSystemMetrics( SM_CYMENU) - 1);
1057 lpitem->rect.right += 2 * menucharsize.cx;
1058 } else {
1059 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1060 RECT tmprc = rc;
1061 LONG tmpheight;
1062 int n = (int)( p - lpitem->text);
1063 /* Item contains a tab (only meaningful in popup menus) */
1064 /* get text size before the tab */
1065 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1066 DT_SINGLELINE|DT_CALCRECT);
1067 txtwidth = rc.right - rc.left;
1068 p += 1; /* advance past the Tab */
1069 /* get text size after the tab */
1070 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1071 DT_SINGLELINE|DT_CALCRECT);
1072 lpitem->xTab += txtwidth;
1073 txtheight = max( txtheight, tmpheight);
1074 txtwidth += menucharsize.cx + /* space for the tab */
1075 tmprc.right - tmprc.left; /* space for the short cut */
1076 } else {
1077 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1078 DT_SINGLELINE|DT_CALCRECT);
1079 txtwidth = rc.right - rc.left;
1080 lpitem->xTab += txtwidth;
1082 lpitem->rect.right += 2 + txtwidth;
1083 itemheight = max( itemheight,
1084 max( txtheight + 2, menucharsize.cy + 4));
1086 if (hfontOld) SelectObject (hdc, hfontOld);
1087 } else if( menuBar) {
1088 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1090 lpitem->rect.bottom += itemheight;
1091 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1095 /***********************************************************************
1096 * MENU_GetMaxPopupHeight
1098 static UINT
1099 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1101 if (lppop->cyMax)
1102 return lppop->cyMax;
1103 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1107 /***********************************************************************
1108 * MENU_PopupMenuCalcSize
1110 * Calculate the size of a popup menu.
1112 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1114 MENUITEM *lpitem;
1115 HDC hdc;
1116 int start, i;
1117 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1119 lppop->Width = lppop->Height = 0;
1120 if (lppop->nItems == 0) return;
1121 hdc = GetDC( 0 );
1123 SelectObject( hdc, get_menu_font(FALSE));
1125 start = 0;
1126 maxX = 2 + 1;
1128 lppop->maxBmpSize.cx = 0;
1129 lppop->maxBmpSize.cy = 0;
1131 while (start < lppop->nItems)
1133 lpitem = &lppop->items[start];
1134 orgX = maxX;
1135 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1136 orgX += MENU_COL_SPACE;
1137 orgY = MENU_TOP_MARGIN;
1139 maxTab = maxTabWidth = 0;
1140 /* Parse items until column break or end of menu */
1141 for (i = start; i < lppop->nItems; i++, lpitem++)
1143 if ((i != start) &&
1144 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1146 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1147 maxX = max( maxX, lpitem->rect.right );
1148 orgY = lpitem->rect.bottom;
1149 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1151 maxTab = max( maxTab, lpitem->xTab );
1152 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1156 /* Finish the column (set all items to the largest width found) */
1157 maxX = max( maxX, maxTab + maxTabWidth );
1158 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1160 lpitem->rect.right = maxX;
1161 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1162 lpitem->xTab = maxTab;
1165 lppop->Height = max( lppop->Height, orgY );
1168 lppop->Width = maxX;
1170 /* space for 3d border */
1171 lppop->Height += MENU_BOTTOM_MARGIN;
1172 lppop->Width += 2;
1174 /* Adjust popup height if it exceeds maximum */
1175 maxHeight = MENU_GetMaxPopupHeight(lppop);
1176 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1177 if (lppop->Height >= maxHeight)
1179 lppop->Height = maxHeight;
1180 lppop->bScrolling = TRUE;
1182 else
1184 lppop->bScrolling = FALSE;
1187 ReleaseDC( 0, hdc );
1191 /***********************************************************************
1192 * MENU_MenuBarCalcSize
1194 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1195 * height is off by 1 pixel which causes lengthy window relocations when
1196 * active document window is maximized/restored.
1198 * Calculate the size of the menu bar.
1200 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1201 LPPOPUPMENU lppop, HWND hwndOwner )
1203 MENUITEM *lpitem;
1204 int start, i, orgX, orgY, maxY, helpPos;
1206 if ((lprect == NULL) || (lppop == NULL)) return;
1207 if (lppop->nItems == 0) return;
1208 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1209 lppop->Width = lprect->right - lprect->left;
1210 lppop->Height = 0;
1211 maxY = lprect->top+1;
1212 start = 0;
1213 helpPos = -1;
1214 lppop->maxBmpSize.cx = 0;
1215 lppop->maxBmpSize.cy = 0;
1216 while (start < lppop->nItems)
1218 lpitem = &lppop->items[start];
1219 orgX = lprect->left;
1220 orgY = maxY;
1222 /* Parse items until line break or end of menu */
1223 for (i = start; i < lppop->nItems; i++, lpitem++)
1225 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1226 if ((i != start) &&
1227 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1229 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1230 debug_print_menuitem (" item: ", lpitem, "");
1231 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1233 if (lpitem->rect.right > lprect->right)
1235 if (i != start) break;
1236 else lpitem->rect.right = lprect->right;
1238 maxY = max( maxY, lpitem->rect.bottom );
1239 orgX = lpitem->rect.right;
1242 /* Finish the line (set all items to the largest height found) */
1243 while (start < i) lppop->items[start++].rect.bottom = maxY;
1246 lprect->bottom = maxY;
1247 lppop->Height = lprect->bottom - lprect->top;
1249 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1250 /* the last item (if several lines, only move the last line) */
1251 lpitem = &lppop->items[lppop->nItems-1];
1252 orgY = lpitem->rect.top;
1253 orgX = lprect->right;
1254 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1255 if ( (helpPos==-1) || (helpPos>i) )
1256 break; /* done */
1257 if (lpitem->rect.top != orgY) break; /* Other line */
1258 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1259 lpitem->rect.left += orgX - lpitem->rect.right;
1260 lpitem->rect.right = orgX;
1261 orgX = lpitem->rect.left;
1266 /***********************************************************************
1267 * MENU_DrawScrollArrows
1269 * Draw scroll arrows.
1271 static void
1272 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1274 HDC hdcMem = CreateCompatibleDC(hdc);
1275 HBITMAP hOrigBitmap;
1276 UINT arrow_bitmap_width, arrow_bitmap_height;
1277 BITMAP bmp;
1278 RECT rect;
1280 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1281 arrow_bitmap_width = bmp.bmWidth;
1282 arrow_bitmap_height = bmp.bmHeight;
1285 if (lppop->nScrollPos)
1286 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1287 else
1288 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1289 rect.left = 0;
1290 rect.top = 0;
1291 rect.right = lppop->Width;
1292 rect.bottom = arrow_bitmap_height;
1293 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1294 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1295 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1296 rect.top = lppop->Height - arrow_bitmap_height;
1297 rect.bottom = lppop->Height;
1298 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1299 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1300 SelectObject(hdcMem, get_down_arrow_bitmap());
1301 else
1302 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1303 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1304 lppop->Height - arrow_bitmap_height,
1305 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1306 SelectObject(hdcMem, hOrigBitmap);
1307 DeleteDC(hdcMem);
1311 /***********************************************************************
1312 * draw_popup_arrow
1314 * Draws the popup-menu arrow.
1316 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1317 UINT arrow_bitmap_height)
1319 HDC hdcMem = CreateCompatibleDC( hdc );
1320 HBITMAP hOrigBitmap;
1322 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1323 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1324 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1325 arrow_bitmap_width, arrow_bitmap_height,
1326 hdcMem, 0, 0, SRCCOPY );
1327 SelectObject( hdcMem, hOrigBitmap );
1328 DeleteDC( hdcMem );
1330 /***********************************************************************
1331 * MENU_DrawMenuItem
1333 * Draw a single menu item.
1335 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1336 UINT height, BOOL menuBar, UINT odaction )
1338 RECT rect;
1339 BOOL flat_menu = FALSE;
1340 int bkgnd;
1341 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1342 POPUPMENU *menu = MENU_GetMenu(hmenu);
1343 RECT bmprc;
1345 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1347 if (!menuBar) {
1348 BITMAP bmp;
1349 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1350 arrow_bitmap_width = bmp.bmWidth;
1351 arrow_bitmap_height = bmp.bmHeight;
1354 if (lpitem->fType & MF_SYSMENU)
1356 if( !IsIconic(hwnd) )
1357 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1358 return;
1361 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1362 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1364 /* Setup colors */
1366 if (lpitem->fState & MF_HILITE)
1368 if(menuBar && !flat_menu) {
1369 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1370 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1371 } else {
1372 if(lpitem->fState & MF_GRAYED)
1373 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1374 else
1375 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1376 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1379 else
1381 if (lpitem->fState & MF_GRAYED)
1382 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1383 else
1384 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1385 SetBkColor( hdc, GetSysColor( bkgnd ) );
1388 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1389 rect = lpitem->rect;
1390 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1392 if (lpitem->fType & MF_OWNERDRAW)
1395 ** Experimentation under Windows reveals that an owner-drawn
1396 ** menu is given the rectangle which includes the space it requested
1397 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1398 ** and a popup-menu arrow. This is the value of lpitem->rect.
1399 ** Windows will leave all drawing to the application except for
1400 ** the popup-menu arrow. Windows always draws that itself, after
1401 ** the menu owner has finished drawing.
1403 DRAWITEMSTRUCT dis;
1405 dis.CtlType = ODT_MENU;
1406 dis.CtlID = 0;
1407 dis.itemID = lpitem->wID;
1408 dis.itemData = lpitem->dwItemData;
1409 dis.itemState = 0;
1410 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1411 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1412 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1413 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1414 dis.hwndItem = (HWND)hmenu;
1415 dis.hDC = hdc;
1416 dis.rcItem = rect;
1417 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1418 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1419 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1420 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1421 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1422 /* Draw the popup-menu arrow */
1423 if (lpitem->fType & MF_POPUP)
1424 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1425 arrow_bitmap_height);
1426 return;
1429 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1431 if (lpitem->fState & MF_HILITE)
1433 if (flat_menu)
1435 InflateRect (&rect, -1, -1);
1436 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1437 InflateRect (&rect, 1, 1);
1438 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1440 else
1442 if(menuBar)
1443 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1444 else
1445 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1448 else
1449 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1451 SetBkMode( hdc, TRANSPARENT );
1453 /* vertical separator */
1454 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1456 HPEN oldPen;
1457 RECT rc = rect;
1459 rc.left -= MENU_COL_SPACE / 2 + 1;
1460 rc.top = 3;
1461 rc.bottom = height - 3;
1462 if (flat_menu)
1464 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1465 MoveToEx( hdc, rc.left, rc.top, NULL );
1466 LineTo( hdc, rc.left, rc.bottom );
1467 SelectObject( hdc, oldPen );
1469 else
1470 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1473 /* horizontal separator */
1474 if (lpitem->fType & MF_SEPARATOR)
1476 HPEN oldPen;
1477 RECT rc = rect;
1479 rc.left++;
1480 rc.right--;
1481 rc.top = ( rc.top + rc.bottom) / 2;
1482 if (flat_menu)
1484 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1485 MoveToEx( hdc, rc.left, rc.top, NULL );
1486 LineTo( hdc, rc.right, rc.top );
1487 SelectObject( hdc, oldPen );
1489 else
1490 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1491 return;
1494 /* helper lines for debugging */
1495 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1496 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1497 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1498 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1501 if (lpitem->hbmpItem) {
1502 /* calculate the bitmap rectangle in coordinates relative
1503 * to the item rectangle */
1504 if( menuBar) {
1505 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1506 bmprc.left = 3;
1507 else
1508 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1509 } else {
1510 bmprc.left = 4;
1511 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1512 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1514 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1515 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1516 bmprc.top = 0;
1517 else
1518 bmprc.top = (rect.bottom - rect.top -
1519 lpitem->bmpsize.cy) / 2;
1520 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1523 if (!menuBar)
1525 HBITMAP bm;
1526 INT y = rect.top + rect.bottom;
1527 RECT rc = rect;
1528 int checked = FALSE;
1529 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1530 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1531 /* Draw the check mark
1533 * FIXME:
1534 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1536 if( !(menu->dwStyle & MNS_NOCHECK)) {
1537 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1538 lpitem->hUnCheckBit;
1539 if (bm) /* we have a custom bitmap */
1541 HDC hdcMem = CreateCompatibleDC( hdc );
1543 SelectObject( hdcMem, bm );
1544 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1545 check_bitmap_width, check_bitmap_height,
1546 hdcMem, 0, 0, SRCCOPY );
1547 DeleteDC( hdcMem );
1548 checked = TRUE;
1550 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1552 RECT r;
1553 HBITMAP bm = CreateBitmap( check_bitmap_width,
1554 check_bitmap_height, 1, 1, NULL );
1555 HDC hdcMem = CreateCompatibleDC( hdc );
1557 SelectObject( hdcMem, bm );
1558 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1559 DrawFrameControl( hdcMem, &r, DFC_MENU,
1560 (lpitem->fType & MFT_RADIOCHECK) ?
1561 DFCS_MENUBULLET : DFCS_MENUCHECK );
1562 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1563 hdcMem, 0, 0, SRCCOPY );
1564 DeleteDC( hdcMem );
1565 DeleteObject( bm );
1566 checked = TRUE;
1569 if( lpitem->hbmpItem &&
1570 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1571 POINT origorg;
1572 /* some applications make this assumption on the DC's origin */
1573 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1574 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1575 odaction, FALSE);
1576 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1578 /* Draw the popup-menu arrow */
1579 if (lpitem->fType & MF_POPUP)
1580 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1581 arrow_bitmap_height);
1582 rect.left += 4;
1583 if( !(menu->dwStyle & MNS_NOCHECK))
1584 rect.left += check_bitmap_width;
1585 rect.right -= arrow_bitmap_width;
1587 else if( lpitem->hbmpItem)
1588 { /* Draw the bitmap */
1589 POINT origorg;
1591 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1592 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1593 odaction, menuBar);
1594 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1596 /* process text if present */
1597 if (lpitem->text)
1599 register int i;
1600 HFONT hfontOld = 0;
1602 UINT uFormat = (menuBar) ?
1603 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1604 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1606 if( !(menu->dwStyle & MNS_CHECKORBMP))
1607 rect.left += menu->maxBmpSize.cx;
1609 if ( lpitem->fState & MFS_DEFAULT )
1611 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1614 if (menuBar) {
1615 if( lpitem->hbmpItem)
1616 rect.left += lpitem->bmpsize.cx;
1617 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1618 rect.left += menucharsize.cx;
1619 rect.right -= menucharsize.cx;
1622 for (i = 0; lpitem->text[i]; i++)
1623 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1624 break;
1626 if(lpitem->fState & MF_GRAYED)
1628 if (!(lpitem->fState & MF_HILITE) )
1630 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1631 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1632 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1633 --rect.left; --rect.top; --rect.right; --rect.bottom;
1635 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1638 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1640 /* paint the shortcut text */
1641 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1643 if (lpitem->text[i] == '\t')
1645 rect.left = lpitem->xTab;
1646 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1648 else
1650 rect.right = lpitem->xTab;
1651 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1654 if(lpitem->fState & MF_GRAYED)
1656 if (!(lpitem->fState & MF_HILITE) )
1658 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1659 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1660 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1661 --rect.left; --rect.top; --rect.right; --rect.bottom;
1663 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1665 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1668 if (hfontOld)
1669 SelectObject (hdc, hfontOld);
1674 /***********************************************************************
1675 * MENU_DrawPopupMenu
1677 * Paint a popup menu.
1679 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1681 HBRUSH hPrevBrush = 0;
1682 RECT rect;
1684 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1686 GetClientRect( hwnd, &rect );
1688 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1689 && (SelectObject( hdc, get_menu_font(FALSE))))
1691 HPEN hPrevPen;
1693 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1695 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1696 if( hPrevPen )
1698 POPUPMENU *menu;
1699 BOOL flat_menu = FALSE;
1701 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1702 if (flat_menu)
1703 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1704 else
1705 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1707 if( (menu = MENU_GetMenu( hmenu )))
1709 /* draw menu items */
1710 if( menu->nItems)
1712 MENUITEM *item;
1713 UINT u;
1715 item = menu->items;
1716 for( u = menu->nItems; u > 0; u--, item++)
1717 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1718 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1720 /* draw scroll arrows */
1721 if (menu->bScrolling)
1722 MENU_DrawScrollArrows(menu, hdc);
1724 } else
1726 SelectObject( hdc, hPrevBrush );
1731 /***********************************************************************
1732 * MENU_DrawMenuBar
1734 * Paint a menu bar. Returns the height of the menu bar.
1735 * called from [windows/nonclient.c]
1737 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1738 BOOL suppress_draw)
1740 LPPOPUPMENU lppop;
1741 HFONT hfontOld = 0;
1742 HMENU hMenu = GetMenu(hwnd);
1744 lppop = MENU_GetMenu( hMenu );
1745 if (lppop == NULL || lprect == NULL)
1747 return GetSystemMetrics(SM_CYMENU);
1750 if (suppress_draw)
1752 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1754 if (lppop->Height == 0)
1755 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1757 lprect->bottom = lprect->top + lppop->Height;
1759 if (hfontOld) SelectObject( hDC, hfontOld);
1760 return lppop->Height;
1762 else
1763 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1767 /***********************************************************************
1768 * MENU_ShowPopup
1770 * Display a popup menu.
1772 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1773 INT x, INT y, INT xanchor, INT yanchor )
1775 POPUPMENU *menu;
1776 INT width, height;
1777 POINT pt;
1778 HMONITOR monitor;
1779 MONITORINFO info;
1781 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1782 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1784 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1785 if (menu->FocusedItem != NO_SELECTED_ITEM)
1787 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1788 menu->FocusedItem = NO_SELECTED_ITEM;
1791 /* store the owner for DrawItem */
1792 menu->hwndOwner = hwndOwner;
1794 menu->nScrollPos = 0;
1795 MENU_PopupMenuCalcSize( menu, hwndOwner );
1797 /* adjust popup menu pos so that it fits within the desktop */
1799 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1800 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1802 /* FIXME: should use item rect */
1803 pt.x = x;
1804 pt.y = y;
1805 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1806 info.cbSize = sizeof(info);
1807 GetMonitorInfoW( monitor, &info );
1808 if( x + width > info.rcWork.right)
1810 if( xanchor && x >= width - xanchor )
1811 x -= width - xanchor;
1813 if( x + width > info.rcWork.right)
1814 x = info.rcWork.right - width;
1816 if( x < info.rcWork.left ) x = info.rcWork.left;
1818 if( y + height > info.rcWork.bottom)
1820 if( yanchor && y >= height + yanchor )
1821 y -= height + yanchor;
1823 if( y + height > info.rcWork.bottom)
1824 y = info.rcWork.bottom - height;
1826 if( y < info.rcWork.top ) y = info.rcWork.top;
1828 /* NOTE: In Windows, top menu popup is not owned. */
1829 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1830 WS_POPUP, x, y, width, height,
1831 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1832 (LPVOID)hmenu );
1833 if( !menu->hWnd ) return FALSE;
1834 if (!top_popup) top_popup = menu->hWnd;
1836 /* Display the window */
1838 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1839 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1840 UpdateWindow( menu->hWnd );
1841 return TRUE;
1845 /***********************************************************************
1846 * MENU_EnsureMenuItemVisible
1848 static void
1849 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1851 if (lppop->bScrolling)
1853 MENUITEM *item = &lppop->items[wIndex];
1854 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1855 UINT nOldPos = lppop->nScrollPos;
1856 RECT rc;
1857 UINT arrow_bitmap_height;
1858 BITMAP bmp;
1860 GetClientRect(lppop->hWnd, &rc);
1862 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1863 arrow_bitmap_height = bmp.bmHeight;
1865 rc.top += arrow_bitmap_height;
1866 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1868 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1869 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1872 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1873 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1874 MENU_DrawScrollArrows(lppop, hdc);
1876 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1878 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1879 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1880 MENU_DrawScrollArrows(lppop, hdc);
1886 /***********************************************************************
1887 * MENU_SelectItem
1889 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1890 BOOL sendMenuSelect, HMENU topmenu )
1892 LPPOPUPMENU lppop;
1893 HDC hdc;
1895 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1897 lppop = MENU_GetMenu( hmenu );
1898 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1900 if (lppop->FocusedItem == wIndex) return;
1901 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1902 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1903 if (!top_popup) top_popup = lppop->hWnd;
1905 SelectObject( hdc, get_menu_font(FALSE));
1907 /* Clear previous highlighted item */
1908 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1910 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1911 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1912 lppop->Height, !(lppop->wFlags & MF_POPUP),
1913 ODA_SELECT );
1916 /* Highlight new item (if any) */
1917 lppop->FocusedItem = wIndex;
1918 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1920 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1921 lppop->items[wIndex].fState |= MF_HILITE;
1922 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1923 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1924 &lppop->items[wIndex], lppop->Height,
1925 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1927 if (sendMenuSelect)
1929 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1930 SendMessageW( hwndOwner, WM_MENUSELECT,
1931 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1932 ip->fType | ip->fState |
1933 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1936 else if (sendMenuSelect) {
1937 if(topmenu){
1938 int pos;
1939 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1940 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1941 MENUITEM *ip = &ptm->items[pos];
1942 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1943 ip->fType | ip->fState |
1944 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1948 ReleaseDC( lppop->hWnd, hdc );
1952 /***********************************************************************
1953 * MENU_MoveSelection
1955 * Moves currently selected item according to the offset parameter.
1956 * If there is no selection then it should select the last item if
1957 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1959 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1961 INT i;
1962 POPUPMENU *menu;
1964 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1966 menu = MENU_GetMenu( hmenu );
1967 if ((!menu) || (!menu->items)) return;
1969 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1971 if( menu->nItems == 1 ) return; else
1972 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1973 ; i += offset)
1974 if (!(menu->items[i].fType & MF_SEPARATOR))
1976 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1977 return;
1981 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1982 i >= 0 && i < menu->nItems ; i += offset)
1983 if (!(menu->items[i].fType & MF_SEPARATOR))
1985 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1986 return;
1991 /**********************************************************************
1992 * MENU_SetItemData
1994 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1995 * ModifyMenu().
1997 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1998 LPCWSTR str )
2000 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2001 TRACE("flags=%x str=%p\n", flags, str);
2003 if (IS_STRING_ITEM(flags))
2005 LPWSTR prevText = item->text;
2006 if (!str)
2008 flags |= MF_SEPARATOR;
2009 item->text = NULL;
2011 else
2013 LPWSTR text;
2014 /* Item beginning with a backspace is a help item */
2015 if (*str == '\b')
2017 flags |= MF_HELP;
2018 str++;
2020 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2021 return FALSE;
2022 strcpyW( text, str );
2023 item->text = text;
2025 item->hbmpItem = NULL;
2026 HeapFree( GetProcessHeap(), 0, prevText );
2028 else if(( flags & MFT_BITMAP)) {
2029 item->hbmpItem = HBITMAP_32(LOWORD(str));
2030 /* setting bitmap clears text */
2031 HeapFree( GetProcessHeap(), 0, item->text );
2032 item->text = NULL;
2035 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2037 if (flags & MF_OWNERDRAW)
2038 item->dwItemData = (DWORD_PTR)str;
2039 else
2040 item->dwItemData = 0;
2042 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2043 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2045 if (flags & MF_POPUP)
2047 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2048 if (menu) menu->wFlags |= MF_POPUP;
2049 else
2051 item->wID = 0;
2052 item->hSubMenu = 0;
2053 item->fType = 0;
2054 item->fState = 0;
2055 return FALSE;
2059 item->wID = id;
2060 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2062 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2063 flags |= MF_POPUP; /* keep popup */
2065 item->fType = flags & TYPE_MASK;
2066 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2067 for ModifyMenu, but Windows accepts it */
2068 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2070 /* Don't call SetRectEmpty here! */
2072 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2073 return TRUE;
2077 /**********************************************************************
2078 * MENU_InsertItem
2080 * Insert (allocate) a new item into a menu.
2082 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2084 MENUITEM *newItems;
2085 POPUPMENU *menu;
2087 if (!(menu = MENU_GetMenu(hMenu)))
2088 return NULL;
2090 /* Find where to insert new item */
2092 if (flags & MF_BYPOSITION) {
2093 if (pos > menu->nItems)
2094 pos = menu->nItems;
2095 } else {
2096 if (!MENU_FindItem( &hMenu, &pos, flags ))
2097 pos = menu->nItems;
2098 else {
2099 if (!(menu = MENU_GetMenu( hMenu )))
2100 return NULL;
2104 /* Make sure that MDI system buttons stay on the right side.
2105 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2106 * regardless of their id.
2108 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2109 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2110 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2111 pos--;
2113 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2115 /* Create new items array */
2117 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2118 if (!newItems)
2120 WARN("allocation failed\n" );
2121 return NULL;
2123 if (menu->nItems > 0)
2125 /* Copy the old array into the new one */
2126 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2127 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2128 (menu->nItems-pos)*sizeof(MENUITEM) );
2129 HeapFree( GetProcessHeap(), 0, menu->items );
2131 menu->items = newItems;
2132 menu->nItems++;
2133 memset( &newItems[pos], 0, sizeof(*newItems) );
2134 menu->Height = 0; /* force size recalculate */
2135 return &newItems[pos];
2139 /**********************************************************************
2140 * MENU_ParseResource
2142 * Parse a standard menu resource and add items to the menu.
2143 * Return a pointer to the end of the resource.
2145 * NOTE: flags is equivalent to the mtOption field
2147 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2149 WORD flags, id = 0;
2150 LPCSTR str;
2151 BOOL end_flag;
2155 flags = GET_WORD(res);
2156 end_flag = flags & MF_END;
2157 /* Remove MF_END because it has the same value as MF_HILITE */
2158 flags &= ~MF_END;
2159 res += sizeof(WORD);
2160 if (!(flags & MF_POPUP))
2162 id = GET_WORD(res);
2163 res += sizeof(WORD);
2165 str = res;
2166 if (!unicode) res += strlen(str) + 1;
2167 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2168 if (flags & MF_POPUP)
2170 HMENU hSubMenu = CreatePopupMenu();
2171 if (!hSubMenu) return NULL;
2172 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2173 return NULL;
2174 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2175 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2177 else /* Not a popup */
2179 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2180 else AppendMenuW( hMenu, flags, id,
2181 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2183 } while (!end_flag);
2184 return res;
2188 /**********************************************************************
2189 * MENUEX_ParseResource
2191 * Parse an extended menu resource and add items to the menu.
2192 * Return a pointer to the end of the resource.
2194 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2196 WORD resinfo;
2197 do {
2198 MENUITEMINFOW mii;
2200 mii.cbSize = sizeof(mii);
2201 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2202 mii.fType = GET_DWORD(res);
2203 res += sizeof(DWORD);
2204 mii.fState = GET_DWORD(res);
2205 res += sizeof(DWORD);
2206 mii.wID = GET_DWORD(res);
2207 res += sizeof(DWORD);
2208 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2209 res += sizeof(WORD);
2210 /* Align the text on a word boundary. */
2211 res += (~((UINT_PTR)res - 1)) & 1;
2212 mii.dwTypeData = (LPWSTR) res;
2213 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2214 /* Align the following fields on a dword boundary. */
2215 res += (~((UINT_PTR)res - 1)) & 3;
2217 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2218 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2220 if (resinfo & 1) { /* Pop-up? */
2221 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2222 res += sizeof(DWORD);
2223 mii.hSubMenu = CreatePopupMenu();
2224 if (!mii.hSubMenu)
2225 return NULL;
2226 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2227 DestroyMenu(mii.hSubMenu);
2228 return NULL;
2230 mii.fMask |= MIIM_SUBMENU;
2231 mii.fType |= MF_POPUP;
2233 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2235 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2236 mii.wID, mii.fType);
2237 mii.fType |= MF_SEPARATOR;
2239 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2240 } while (!(resinfo & MF_END));
2241 return res;
2245 /***********************************************************************
2246 * MENU_GetSubPopup
2248 * Return the handle of the selected sub-popup menu (if any).
2250 static HMENU MENU_GetSubPopup( HMENU hmenu )
2252 POPUPMENU *menu;
2253 MENUITEM *item;
2255 menu = MENU_GetMenu( hmenu );
2257 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2259 item = &menu->items[menu->FocusedItem];
2260 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2261 return item->hSubMenu;
2262 return 0;
2266 /***********************************************************************
2267 * MENU_HideSubPopups
2269 * Hide the sub-popup menus of this menu.
2271 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2272 BOOL sendMenuSelect )
2274 POPUPMENU *menu = MENU_GetMenu( hmenu );
2276 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2278 if (menu && top_popup)
2280 HMENU hsubmenu;
2281 POPUPMENU *submenu;
2282 MENUITEM *item;
2284 if (menu->FocusedItem != NO_SELECTED_ITEM)
2286 item = &menu->items[menu->FocusedItem];
2287 if (!(item->fType & MF_POPUP) ||
2288 !(item->fState & MF_MOUSESELECT)) return;
2289 item->fState &= ~MF_MOUSESELECT;
2290 hsubmenu = item->hSubMenu;
2291 } else return;
2293 submenu = MENU_GetMenu( hsubmenu );
2294 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2295 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2296 DestroyWindow( submenu->hWnd );
2297 submenu->hWnd = 0;
2302 /***********************************************************************
2303 * MENU_ShowSubPopup
2305 * Display the sub-menu of the selected item of this menu.
2306 * Return the handle of the submenu, or hmenu if no submenu to display.
2308 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2309 BOOL selectFirst, UINT wFlags )
2311 RECT rect;
2312 POPUPMENU *menu;
2313 MENUITEM *item;
2314 HDC hdc;
2316 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2318 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2320 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2322 item = &menu->items[menu->FocusedItem];
2323 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2324 return hmenu;
2326 /* message must be sent before using item,
2327 because nearly everything may be changed by the application ! */
2329 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2330 if (!(wFlags & TPM_NONOTIFY))
2331 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2332 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2334 item = &menu->items[menu->FocusedItem];
2335 rect = item->rect;
2337 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2338 if (!(item->fState & MF_HILITE))
2340 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2341 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2343 SelectObject( hdc, get_menu_font(FALSE));
2345 item->fState |= MF_HILITE;
2346 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2347 ReleaseDC( menu->hWnd, hdc );
2349 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2350 item->rect = rect;
2352 item->fState |= MF_MOUSESELECT;
2354 if (IS_SYSTEM_MENU(menu))
2356 MENU_InitSysMenuPopup(item->hSubMenu,
2357 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2358 GetClassLongW( menu->hWnd, GCL_STYLE));
2360 NC_GetSysPopupPos( menu->hWnd, &rect );
2361 rect.top = rect.bottom;
2362 rect.right = GetSystemMetrics(SM_CXSIZE);
2363 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2365 else
2367 GetWindowRect( menu->hWnd, &rect );
2368 if (menu->wFlags & MF_POPUP)
2370 RECT rc = item->rect;
2372 MENU_AdjustMenuItemRect(menu, &rc);
2374 /* The first item in the popup menu has to be at the
2375 same y position as the focused menu item */
2376 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2377 rect.top += rc.top - MENU_TOP_MARGIN;
2378 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2379 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2380 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2382 else
2384 rect.left += item->rect.left;
2385 rect.top += item->rect.bottom;
2386 rect.right = item->rect.right - item->rect.left;
2387 rect.bottom = item->rect.bottom - item->rect.top;
2391 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2392 rect.left, rect.top, rect.right, rect.bottom );
2393 if (selectFirst)
2394 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2395 return item->hSubMenu;
2400 /**********************************************************************
2401 * MENU_IsMenuActive
2403 HWND MENU_IsMenuActive(void)
2405 return top_popup;
2408 /***********************************************************************
2409 * MENU_PtMenu
2411 * Walks menu chain trying to find a menu pt maps to.
2413 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2415 POPUPMENU *menu = MENU_GetMenu( hMenu );
2416 UINT item = menu->FocusedItem;
2417 HMENU ret;
2419 /* try subpopup first (if any) */
2420 ret = (item != NO_SELECTED_ITEM &&
2421 (menu->items[item].fType & MF_POPUP) &&
2422 (menu->items[item].fState & MF_MOUSESELECT))
2423 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2425 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2427 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2428 if( menu->wFlags & MF_POPUP )
2430 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2432 else if (ht == HTSYSMENU)
2433 ret = get_win_sys_menu( menu->hWnd );
2434 else if (ht == HTMENU)
2435 ret = GetMenu( menu->hWnd );
2437 return ret;
2440 /***********************************************************************
2441 * MENU_ExecFocusedItem
2443 * Execute a menu item (for instance when user pressed Enter).
2444 * Return the wID of the executed item. Otherwise, -1 indicating
2445 * that no menu item was executed, -2 if a popup is shown;
2446 * Have to receive the flags for the TrackPopupMenu options to avoid
2447 * sending unwanted message.
2450 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2452 MENUITEM *item;
2453 POPUPMENU *menu = MENU_GetMenu( hMenu );
2455 TRACE("%p hmenu=%p\n", pmt, hMenu);
2457 if (!menu || !menu->nItems ||
2458 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2460 item = &menu->items[menu->FocusedItem];
2462 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2464 if (!(item->fType & MF_POPUP))
2466 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2468 /* If TPM_RETURNCMD is set you return the id, but
2469 do not send a message to the owner */
2470 if(!(wFlags & TPM_RETURNCMD))
2472 if( menu->wFlags & MF_SYSMENU )
2473 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2474 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2475 else
2477 if (menu->dwStyle & MNS_NOTIFYBYPOS)
2478 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2479 (LPARAM)hMenu);
2480 else
2481 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2484 return item->wID;
2487 else
2489 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2490 return -2;
2493 return -1;
2496 /***********************************************************************
2497 * MENU_SwitchTracking
2499 * Helper function for menu navigation routines.
2501 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2503 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2504 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2506 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2508 if( pmt->hTopMenu != hPtMenu &&
2509 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2511 /* both are top level menus (system and menu-bar) */
2512 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2513 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2514 pmt->hTopMenu = hPtMenu;
2516 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2517 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2521 /***********************************************************************
2522 * MENU_ButtonDown
2524 * Return TRUE if we can go on with menu tracking.
2526 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2528 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2530 if (hPtMenu)
2532 UINT id = 0;
2533 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2534 MENUITEM *item;
2536 if( IS_SYSTEM_MENU(ptmenu) )
2537 item = ptmenu->items;
2538 else
2539 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2541 if( item )
2543 if( ptmenu->FocusedItem != id )
2544 MENU_SwitchTracking( pmt, hPtMenu, id );
2546 /* If the popup menu is not already "popped" */
2547 if(!(item->fState & MF_MOUSESELECT ))
2549 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2552 return TRUE;
2554 /* Else the click was on the menu bar, finish the tracking */
2556 return FALSE;
2559 /***********************************************************************
2560 * MENU_ButtonUp
2562 * Return the value of MENU_ExecFocusedItem if
2563 * the selected item was not a popup. Else open the popup.
2564 * A -1 return value indicates that we go on with menu tracking.
2567 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2569 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2571 if (hPtMenu)
2573 UINT id = 0;
2574 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2575 MENUITEM *item;
2577 if( IS_SYSTEM_MENU(ptmenu) )
2578 item = ptmenu->items;
2579 else
2580 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2582 if( item && (ptmenu->FocusedItem == id ))
2584 debug_print_menuitem ("FocusedItem: ", item, "");
2586 if( !(item->fType & MF_POPUP) )
2588 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2589 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2590 return executedMenuId;
2593 /* If we are dealing with the top-level menu */
2594 /* and this is a click on an already "popped" item: */
2595 /* Stop the menu tracking and close the opened submenus */
2596 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2597 return 0;
2599 ptmenu->bTimeToHide = TRUE;
2601 return -1;
2605 /***********************************************************************
2606 * MENU_MouseMove
2608 * Return TRUE if we can go on with menu tracking.
2610 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2612 UINT id = NO_SELECTED_ITEM;
2613 POPUPMENU *ptmenu = NULL;
2615 if( hPtMenu )
2617 ptmenu = MENU_GetMenu( hPtMenu );
2618 if( IS_SYSTEM_MENU(ptmenu) )
2619 id = 0;
2620 else
2621 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2624 if( id == NO_SELECTED_ITEM )
2626 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2627 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2630 else if( ptmenu->FocusedItem != id )
2632 MENU_SwitchTracking( pmt, hPtMenu, id );
2633 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2635 return TRUE;
2639 /***********************************************************************
2640 * MENU_SetCapture
2642 static void MENU_SetCapture( HWND hwnd )
2644 HWND previous = 0;
2646 SERVER_START_REQ( set_capture_window )
2648 req->handle = hwnd;
2649 req->flags = CAPTURE_MENU;
2650 if (!wine_server_call_err( req ))
2652 previous = reply->previous;
2653 hwnd = reply->full_handle;
2656 SERVER_END_REQ;
2658 if (previous && previous != hwnd)
2659 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2663 /***********************************************************************
2664 * MENU_DoNextMenu
2666 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2668 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2670 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2671 BOOL atEnd = FALSE;
2673 /* When skipping left, we need to do something special after the
2674 first menu. */
2675 if (vk == VK_LEFT && menu->FocusedItem == 0)
2677 atEnd = TRUE;
2679 /* When skipping right, for the non-system menu, we need to
2680 handle the last non-special menu item (ie skip any window
2681 icons such as MDI maximize, restore or close) */
2682 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2684 int i = menu->FocusedItem + 1;
2685 while (i < (menu->nItems - 1)) {
2686 if ((menu->items[i].wID >= SC_SIZE &&
2687 menu->items[i].wID <= SC_RESTORE)) {
2688 i++;
2689 } else break;
2691 if (i == (menu->nItems - 1)) {
2692 atEnd = TRUE;
2695 /* When skipping right, we need to cater for the system menu */
2696 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2698 if (menu->FocusedItem == (menu->nItems - 1)) {
2699 atEnd = TRUE;
2703 if( atEnd )
2705 MDINEXTMENU next_menu;
2706 HMENU hNewMenu;
2707 HWND hNewWnd;
2708 UINT id = 0;
2710 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2711 next_menu.hmenuNext = 0;
2712 next_menu.hwndNext = 0;
2713 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2715 TRACE("%p [%p] -> %p [%p]\n",
2716 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2718 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2720 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2721 hNewWnd = pmt->hOwnerWnd;
2722 if( IS_SYSTEM_MENU(menu) )
2724 /* switch to the menu bar */
2726 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2728 if( vk == VK_LEFT )
2730 menu = MENU_GetMenu( hNewMenu );
2731 id = menu->nItems - 1;
2733 /* Skip backwards over any system predefined icons,
2734 eg. MDI close, restore etc icons */
2735 while ((id > 0) &&
2736 (menu->items[id].wID >= SC_SIZE &&
2737 menu->items[id].wID <= SC_RESTORE)) id--;
2740 else if (style & WS_SYSMENU )
2742 /* switch to the system menu */
2743 hNewMenu = get_win_sys_menu( hNewWnd );
2745 else return FALSE;
2747 else /* application returned a new menu to switch to */
2749 hNewMenu = next_menu.hmenuNext;
2750 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2752 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2754 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2756 if (style & WS_SYSMENU &&
2757 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2759 /* get the real system menu */
2760 hNewMenu = get_win_sys_menu(hNewWnd);
2762 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2764 /* FIXME: Not sure what to do here;
2765 * perhaps try to track hNewMenu as a popup? */
2767 TRACE(" -- got confused.\n");
2768 return FALSE;
2771 else return FALSE;
2774 if( hNewMenu != pmt->hTopMenu )
2776 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2777 FALSE, 0 );
2778 if( pmt->hCurrentMenu != pmt->hTopMenu )
2779 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2782 if( hNewWnd != pmt->hOwnerWnd )
2784 pmt->hOwnerWnd = hNewWnd;
2785 MENU_SetCapture( pmt->hOwnerWnd );
2788 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2789 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2791 return TRUE;
2793 return FALSE;
2796 /***********************************************************************
2797 * MENU_SuspendPopup
2799 * The idea is not to show the popup if the next input message is
2800 * going to hide it anyway.
2802 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2804 MSG msg;
2806 msg.hwnd = pmt->hOwnerWnd;
2808 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2809 pmt->trackFlags |= TF_SKIPREMOVE;
2811 switch( uMsg )
2813 case WM_KEYDOWN:
2814 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2815 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2817 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2818 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2819 if( msg.message == WM_KEYDOWN &&
2820 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2822 pmt->trackFlags |= TF_SUSPENDPOPUP;
2823 return TRUE;
2826 break;
2829 /* failures go through this */
2830 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2831 return FALSE;
2834 /***********************************************************************
2835 * MENU_KeyEscape
2837 * Handle a VK_ESCAPE key event in a menu.
2839 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2841 BOOL bEndMenu = TRUE;
2843 if (pmt->hCurrentMenu != pmt->hTopMenu)
2845 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2847 if (menu->wFlags & MF_POPUP)
2849 HMENU hmenutmp, hmenuprev;
2851 hmenuprev = hmenutmp = pmt->hTopMenu;
2853 /* close topmost popup */
2854 while (hmenutmp != pmt->hCurrentMenu)
2856 hmenuprev = hmenutmp;
2857 hmenutmp = MENU_GetSubPopup( hmenuprev );
2860 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2861 pmt->hCurrentMenu = hmenuprev;
2862 bEndMenu = FALSE;
2866 return bEndMenu;
2869 /***********************************************************************
2870 * MENU_KeyLeft
2872 * Handle a VK_LEFT key event in a menu.
2874 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2876 POPUPMENU *menu;
2877 HMENU hmenutmp, hmenuprev;
2878 UINT prevcol;
2880 hmenuprev = hmenutmp = pmt->hTopMenu;
2881 menu = MENU_GetMenu( hmenutmp );
2883 /* Try to move 1 column left (if possible) */
2884 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2885 NO_SELECTED_ITEM ) {
2887 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2888 prevcol, TRUE, 0 );
2889 return;
2892 /* close topmost popup */
2893 while (hmenutmp != pmt->hCurrentMenu)
2895 hmenuprev = hmenutmp;
2896 hmenutmp = MENU_GetSubPopup( hmenuprev );
2899 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2900 pmt->hCurrentMenu = hmenuprev;
2902 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2904 /* move menu bar selection if no more popups are left */
2906 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2907 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2909 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2911 /* A sublevel menu was displayed - display the next one
2912 * unless there is another displacement coming up */
2914 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2915 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2916 pmt->hTopMenu, TRUE, wFlags);
2922 /***********************************************************************
2923 * MENU_KeyRight
2925 * Handle a VK_RIGHT key event in a menu.
2927 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2929 HMENU hmenutmp;
2930 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2931 UINT nextcol;
2933 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2934 pmt->hCurrentMenu,
2935 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2936 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2938 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2940 /* If already displaying a popup, try to display sub-popup */
2942 hmenutmp = pmt->hCurrentMenu;
2943 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2945 /* if subpopup was displayed then we are done */
2946 if (hmenutmp != pmt->hCurrentMenu) return;
2949 /* Check to see if there's another column */
2950 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2951 NO_SELECTED_ITEM ) {
2952 TRACE("Going to %d.\n", nextcol );
2953 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2954 nextcol, TRUE, 0 );
2955 return;
2958 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2960 if( pmt->hCurrentMenu != pmt->hTopMenu )
2962 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2963 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2964 } else hmenutmp = 0;
2966 /* try to move to the next item */
2967 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2968 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2970 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2971 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2972 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2973 pmt->hTopMenu, TRUE, wFlags);
2977 /***********************************************************************
2978 * MENU_TrackMenu
2980 * Menu tracking code.
2982 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2983 HWND hwnd, const RECT *lprect )
2985 MSG msg;
2986 POPUPMENU *menu;
2987 BOOL fRemove;
2988 INT executedMenuId = -1;
2989 MTRACKER mt;
2990 BOOL enterIdleSent = FALSE;
2992 mt.trackFlags = 0;
2993 mt.hCurrentMenu = hmenu;
2994 mt.hTopMenu = hmenu;
2995 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2996 mt.pt.x = x;
2997 mt.pt.y = y;
2999 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3000 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3002 fEndMenu = FALSE;
3003 if (!(menu = MENU_GetMenu( hmenu )))
3005 WARN("Invalid menu handle %p\n", hmenu);
3006 SetLastError(ERROR_INVALID_MENU_HANDLE);
3007 return FALSE;
3010 if (wFlags & TPM_BUTTONDOWN)
3012 /* Get the result in order to start the tracking or not */
3013 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3014 fEndMenu = !fRemove;
3017 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3019 MENU_SetCapture( mt.hOwnerWnd );
3021 while (!fEndMenu)
3023 menu = MENU_GetMenu( mt.hCurrentMenu );
3024 if (!menu) /* sometimes happens if I do a window manager close */
3025 break;
3027 /* we have to keep the message in the queue until it's
3028 * clear that menu loop is not over yet. */
3030 for (;;)
3032 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3034 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3035 /* remove the message from the queue */
3036 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3038 else
3040 if (!enterIdleSent)
3042 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3043 enterIdleSent = TRUE;
3044 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3046 WaitMessage();
3050 /* check if EndMenu() tried to cancel us, by posting this message */
3051 if(msg.message == WM_CANCELMODE)
3053 /* we are now out of the loop */
3054 fEndMenu = TRUE;
3056 /* remove the message from the queue */
3057 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3059 /* break out of internal loop, ala ESCAPE */
3060 break;
3063 TranslateMessage( &msg );
3064 mt.pt = msg.pt;
3066 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3067 enterIdleSent=FALSE;
3069 fRemove = FALSE;
3070 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3073 * Use the mouse coordinates in lParam instead of those in the MSG
3074 * struct to properly handle synthetic messages. They are already
3075 * in screen coordinates.
3077 mt.pt.x = (short)LOWORD(msg.lParam);
3078 mt.pt.y = (short)HIWORD(msg.lParam);
3080 /* Find a menu for this mouse event */
3081 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3083 switch(msg.message)
3085 /* no WM_NC... messages in captured state */
3087 case WM_RBUTTONDBLCLK:
3088 case WM_RBUTTONDOWN:
3089 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3090 /* fall through */
3091 case WM_LBUTTONDBLCLK:
3092 case WM_LBUTTONDOWN:
3093 /* If the message belongs to the menu, removes it from the queue */
3094 /* Else, end menu tracking */
3095 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3096 fEndMenu = !fRemove;
3097 break;
3099 case WM_RBUTTONUP:
3100 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3101 /* fall through */
3102 case WM_LBUTTONUP:
3103 /* Check if a menu was selected by the mouse */
3104 if (hmenu)
3106 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3107 TRACE("executedMenuId %d\n", executedMenuId);
3109 /* End the loop if executedMenuId is an item ID */
3110 /* or if the job was done (executedMenuId = 0). */
3111 fEndMenu = fRemove = (executedMenuId != -1);
3113 /* No menu was selected by the mouse */
3114 /* if the function was called by TrackPopupMenu, continue
3115 with the menu tracking. If not, stop it */
3116 else
3117 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3119 break;
3121 case WM_MOUSEMOVE:
3122 /* the selected menu item must be changed every time */
3123 /* the mouse moves. */
3125 if (hmenu)
3126 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3128 } /* switch(msg.message) - mouse */
3130 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3132 fRemove = TRUE; /* Keyboard messages are always removed */
3133 switch(msg.message)
3135 case WM_KEYDOWN:
3136 case WM_SYSKEYDOWN:
3137 switch(msg.wParam)
3139 case VK_MENU:
3140 fEndMenu = TRUE;
3141 break;
3143 case VK_HOME:
3144 case VK_END:
3145 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3146 NO_SELECTED_ITEM, FALSE, 0 );
3147 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3148 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3149 break;
3151 case VK_UP:
3152 case VK_DOWN: /* If on menu bar, pull-down the menu */
3154 menu = MENU_GetMenu( mt.hCurrentMenu );
3155 if (!(menu->wFlags & MF_POPUP))
3156 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3157 else /* otherwise try to move selection */
3158 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3159 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3160 break;
3162 case VK_LEFT:
3163 MENU_KeyLeft( &mt, wFlags );
3164 break;
3166 case VK_RIGHT:
3167 MENU_KeyRight( &mt, wFlags );
3168 break;
3170 case VK_ESCAPE:
3171 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3172 break;
3174 case VK_F1:
3176 HELPINFO hi;
3177 hi.cbSize = sizeof(HELPINFO);
3178 hi.iContextType = HELPINFO_MENUITEM;
3179 if (menu->FocusedItem == NO_SELECTED_ITEM)
3180 hi.iCtrlId = 0;
3181 else
3182 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3183 hi.hItemHandle = hmenu;
3184 hi.dwContextId = menu->dwContextHelpID;
3185 hi.MousePos = msg.pt;
3186 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3187 break;
3190 default:
3191 break;
3193 break; /* WM_KEYDOWN */
3195 case WM_CHAR:
3196 case WM_SYSCHAR:
3198 UINT pos;
3200 if (msg.wParam == '\r' || msg.wParam == ' ')
3202 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3203 fEndMenu = (executedMenuId != -2);
3205 break;
3208 /* Hack to avoid control chars. */
3209 /* We will find a better way real soon... */
3210 if (msg.wParam < 32) break;
3212 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3213 LOWORD(msg.wParam), FALSE );
3214 if (pos == (UINT)-2) fEndMenu = TRUE;
3215 else if (pos == (UINT)-1) MessageBeep(0);
3216 else
3218 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3219 TRUE, 0 );
3220 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3221 fEndMenu = (executedMenuId != -2);
3224 break;
3225 } /* switch(msg.message) - kbd */
3227 else
3229 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3230 DispatchMessageW( &msg );
3231 continue;
3234 if (!fEndMenu) fRemove = TRUE;
3236 /* finally remove message from the queue */
3238 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3239 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3240 else mt.trackFlags &= ~TF_SKIPREMOVE;
3243 MENU_SetCapture(0); /* release the capture */
3245 /* If dropdown is still painted and the close box is clicked on
3246 then the menu will be destroyed as part of the DispatchMessage above.
3247 This will then invalidate the menu handle in mt.hTopMenu. We should
3248 check for this first. */
3249 if( IsMenu( mt.hTopMenu ) )
3251 menu = MENU_GetMenu( mt.hTopMenu );
3253 if( IsWindow( mt.hOwnerWnd ) )
3255 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3257 if (menu && (menu->wFlags & MF_POPUP))
3259 DestroyWindow( menu->hWnd );
3260 menu->hWnd = 0;
3262 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3263 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3266 /* Reset the variable for hiding menu */
3267 if( menu ) menu->bTimeToHide = FALSE;
3270 /* The return value is only used by TrackPopupMenu */
3271 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3272 if (executedMenuId == -1) executedMenuId = 0;
3273 return executedMenuId;
3276 /***********************************************************************
3277 * MENU_InitTracking
3279 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3281 POPUPMENU *menu;
3283 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3285 HideCaret(0);
3287 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3288 if (!(wFlags & TPM_NONOTIFY))
3289 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3291 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3293 if (!(wFlags & TPM_NONOTIFY))
3295 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3296 /* If an app changed/recreated menu bar entries in WM_INITMENU
3297 * menu sizes will be recalculated once the menu created/shown.
3301 /* This makes the menus of applications built with Delphi work.
3302 * It also enables menus to be displayed in more than one window,
3303 * but there are some bugs left that need to be fixed in this case.
3305 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3307 return TRUE;
3309 /***********************************************************************
3310 * MENU_ExitTracking
3312 static BOOL MENU_ExitTracking(HWND hWnd)
3314 TRACE("hwnd=%p\n", hWnd);
3316 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3317 ShowCaret(0);
3318 top_popup = 0;
3319 return TRUE;
3322 /***********************************************************************
3323 * MENU_TrackMouseMenuBar
3325 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3327 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3329 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3330 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3332 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3334 if (IsMenu(hMenu))
3336 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3337 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3338 MENU_ExitTracking(hWnd);
3343 /***********************************************************************
3344 * MENU_TrackKbdMenuBar
3346 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3348 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3350 UINT uItem = NO_SELECTED_ITEM;
3351 HMENU hTrackMenu;
3352 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3354 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3356 /* find window that has a menu */
3358 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3359 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3361 /* check if we have to track a system menu */
3363 hTrackMenu = GetMenu( hwnd );
3364 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3366 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3367 hTrackMenu = get_win_sys_menu( hwnd );
3368 uItem = 0;
3369 wParam |= HTSYSMENU; /* prevent item lookup */
3372 if (!IsMenu( hTrackMenu )) return;
3374 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3376 if( wChar && wChar != ' ' )
3378 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3379 if ( uItem >= (UINT)(-2) )
3381 if( uItem == (UINT)(-1) ) MessageBeep(0);
3382 /* schedule end of menu tracking */
3383 wFlags |= TF_ENDMENU;
3384 goto track_menu;
3388 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3390 if (wParam & HTSYSMENU)
3392 /* prevent sysmenu activation for managed windows on Alt down/up */
3393 if (GetPropA( hwnd, "__wine_x11_managed" ))
3394 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3396 else
3398 if( uItem == NO_SELECTED_ITEM )
3399 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3400 else
3401 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3404 track_menu:
3405 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3406 MENU_ExitTracking( hwnd );
3410 /**********************************************************************
3411 * TrackPopupMenu (USER32.@)
3413 * Like the win32 API, the function return the command ID only if the
3414 * flag TPM_RETURNCMD is on.
3417 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3418 INT nReserved, HWND hWnd, const RECT *lpRect )
3420 BOOL ret = FALSE;
3422 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3423 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3425 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3427 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3428 if (!(wFlags & TPM_NONOTIFY))
3429 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3431 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3432 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3433 MENU_ExitTracking(hWnd);
3435 return ret;
3438 /**********************************************************************
3439 * TrackPopupMenuEx (USER32.@)
3441 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3442 HWND hWnd, LPTPMPARAMS lpTpm )
3444 FIXME("not fully implemented\n" );
3445 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3446 lpTpm ? &lpTpm->rcExclude : NULL );
3449 /***********************************************************************
3450 * PopupMenuWndProc
3452 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3454 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3456 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3458 switch(message)
3460 case WM_CREATE:
3462 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3463 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3464 return 0;
3467 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3468 return MA_NOACTIVATE;
3470 case WM_PAINT:
3472 PAINTSTRUCT ps;
3473 BeginPaint( hwnd, &ps );
3474 MENU_DrawPopupMenu( hwnd, ps.hdc,
3475 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3476 EndPaint( hwnd, &ps );
3477 return 0;
3479 case WM_ERASEBKGND:
3480 return 1;
3482 case WM_DESTROY:
3483 /* zero out global pointer in case resident popup window was destroyed. */
3484 if (hwnd == top_popup) top_popup = 0;
3485 break;
3487 case WM_SHOWWINDOW:
3489 if( wParam )
3491 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3493 else
3494 SetWindowLongPtrW( hwnd, 0, 0 );
3495 break;
3497 case MM_SETMENUHANDLE:
3498 SetWindowLongPtrW( hwnd, 0, wParam );
3499 break;
3501 case MM_GETMENUHANDLE:
3502 return GetWindowLongPtrW( hwnd, 0 );
3504 default:
3505 return DefWindowProcW( hwnd, message, wParam, lParam );
3507 return 0;
3511 /***********************************************************************
3512 * MENU_GetMenuBarHeight
3514 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3516 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3517 INT orgX, INT orgY )
3519 HDC hdc;
3520 RECT rectBar;
3521 LPPOPUPMENU lppop;
3523 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3525 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3527 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3528 SelectObject( hdc, get_menu_font(FALSE));
3529 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3530 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3531 ReleaseDC( hwnd, hdc );
3532 return lppop->Height;
3536 /*******************************************************************
3537 * ChangeMenuA (USER32.@)
3539 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3540 UINT id, UINT flags )
3542 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3543 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3544 id, data );
3545 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3546 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3547 id, data );
3548 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3549 flags & MF_BYPOSITION ? pos : id,
3550 flags & ~MF_REMOVE );
3551 /* Default: MF_INSERT */
3552 return InsertMenuA( hMenu, pos, flags, id, data );
3556 /*******************************************************************
3557 * ChangeMenuW (USER32.@)
3559 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3560 UINT id, UINT flags )
3562 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3563 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3564 id, data );
3565 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3566 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3567 id, data );
3568 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3569 flags & MF_BYPOSITION ? pos : id,
3570 flags & ~MF_REMOVE );
3571 /* Default: MF_INSERT */
3572 return InsertMenuW( hMenu, pos, flags, id, data );
3576 /*******************************************************************
3577 * CheckMenuItem (USER32.@)
3579 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3581 MENUITEM *item;
3582 DWORD ret;
3584 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3585 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3586 ret = item->fState & MF_CHECKED;
3587 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3588 else item->fState &= ~MF_CHECKED;
3589 return ret;
3593 /**********************************************************************
3594 * EnableMenuItem (USER32.@)
3596 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3598 UINT oldflags;
3599 MENUITEM *item;
3600 POPUPMENU *menu;
3602 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3604 /* Get the Popupmenu to access the owner menu */
3605 if (!(menu = MENU_GetMenu(hMenu)))
3606 return (UINT)-1;
3608 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3609 return (UINT)-1;
3611 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3612 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3614 /* If the close item in the system menu change update the close button */
3615 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3617 if (menu->hSysMenuOwner != 0)
3619 RECT rc;
3620 POPUPMENU* parentMenu;
3622 /* Get the parent menu to access*/
3623 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3624 return (UINT)-1;
3626 /* Refresh the frame to reflect the change */
3627 GetWindowRect(parentMenu->hWnd, &rc);
3628 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3629 rc.bottom = 0;
3630 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3634 return oldflags;
3638 /*******************************************************************
3639 * GetMenuStringA (USER32.@)
3641 INT WINAPI GetMenuStringA(
3642 HMENU hMenu, /* [in] menuhandle */
3643 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3644 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3645 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3646 UINT wFlags /* [in] MF_ flags */
3648 MENUITEM *item;
3650 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3651 if (str && nMaxSiz) str[0] = '\0';
3652 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3653 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3654 return 0;
3656 if (!item->text) return 0;
3657 if (!str || !nMaxSiz) return strlenW(item->text);
3658 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3659 str[nMaxSiz-1] = 0;
3660 TRACE("returning '%s'\n", str );
3661 return strlen(str);
3665 /*******************************************************************
3666 * GetMenuStringW (USER32.@)
3668 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3669 LPWSTR str, INT nMaxSiz, UINT wFlags )
3671 MENUITEM *item;
3673 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3674 if (str && nMaxSiz) str[0] = '\0';
3675 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3676 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3677 return 0;
3679 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3680 if( !(item->text)) {
3681 str[0] = 0;
3682 return 0;
3684 lstrcpynW( str, item->text, nMaxSiz );
3685 return strlenW(str);
3689 /**********************************************************************
3690 * HiliteMenuItem (USER32.@)
3692 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3693 UINT wHilite )
3695 LPPOPUPMENU menu;
3696 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3697 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3698 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3699 if (menu->FocusedItem == wItemID) return TRUE;
3700 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3701 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3702 return TRUE;
3706 /**********************************************************************
3707 * GetMenuState (USER32.@)
3709 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3711 MENUITEM *item;
3712 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3713 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3714 debug_print_menuitem (" item: ", item, "");
3715 if (item->fType & MF_POPUP)
3717 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3718 if (!menu) return -1;
3719 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3721 else
3723 /* We used to (from way back then) mask the result to 0xff. */
3724 /* I don't know why and it seems wrong as the documented */
3725 /* return flag MF_SEPARATOR is outside that mask. */
3726 return (item->fType | item->fState);
3731 /**********************************************************************
3732 * GetMenuItemCount (USER32.@)
3734 INT WINAPI GetMenuItemCount( HMENU hMenu )
3736 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3737 if (!menu) return -1;
3738 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3739 return menu->nItems;
3743 /**********************************************************************
3744 * GetMenuItemID (USER32.@)
3746 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3748 MENUITEM * lpmi;
3750 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3751 if (lpmi->fType & MF_POPUP) return -1;
3752 return lpmi->wID;
3757 /*******************************************************************
3758 * InsertMenuW (USER32.@)
3760 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3761 UINT_PTR id, LPCWSTR str )
3763 MENUITEM *item;
3765 if (IS_STRING_ITEM(flags) && str)
3766 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3767 hMenu, pos, flags, id, debugstr_w(str) );
3768 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3769 hMenu, pos, flags, id, str );
3771 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3773 if (!(MENU_SetItemData( item, flags, id, str )))
3775 RemoveMenu( hMenu, pos, flags );
3776 return FALSE;
3779 item->hCheckBit = item->hUnCheckBit = 0;
3780 return TRUE;
3784 /*******************************************************************
3785 * InsertMenuA (USER32.@)
3787 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3788 UINT_PTR id, LPCSTR str )
3790 BOOL ret = FALSE;
3792 if (IS_STRING_ITEM(flags) && str)
3794 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3795 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3796 if (newstr)
3798 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3799 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3800 HeapFree( GetProcessHeap(), 0, newstr );
3802 return ret;
3804 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3808 /*******************************************************************
3809 * AppendMenuA (USER32.@)
3811 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3812 UINT_PTR id, LPCSTR data )
3814 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3818 /*******************************************************************
3819 * AppendMenuW (USER32.@)
3821 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3822 UINT_PTR id, LPCWSTR data )
3824 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3828 /**********************************************************************
3829 * RemoveMenu (USER32.@)
3831 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3833 LPPOPUPMENU menu;
3834 MENUITEM *item;
3836 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3837 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3838 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3840 /* Remove item */
3842 MENU_FreeItemData( item );
3844 if (--menu->nItems == 0)
3846 HeapFree( GetProcessHeap(), 0, menu->items );
3847 menu->items = NULL;
3849 else
3851 while(nPos < menu->nItems)
3853 *item = *(item+1);
3854 item++;
3855 nPos++;
3857 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3858 menu->nItems * sizeof(MENUITEM) );
3860 return TRUE;
3864 /**********************************************************************
3865 * DeleteMenu (USER32.@)
3867 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3869 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3870 if (!item) return FALSE;
3871 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3872 /* nPos is now the position of the item */
3873 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3874 return TRUE;
3878 /*******************************************************************
3879 * ModifyMenuW (USER32.@)
3881 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3882 UINT_PTR id, LPCWSTR str )
3884 MENUITEM *item;
3886 if (IS_STRING_ITEM(flags))
3887 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3888 else
3889 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3891 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3892 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3893 return MENU_SetItemData( item, flags, id, str );
3897 /*******************************************************************
3898 * ModifyMenuA (USER32.@)
3900 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3901 UINT_PTR id, LPCSTR str )
3903 BOOL ret = FALSE;
3905 if (IS_STRING_ITEM(flags) && str)
3907 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3908 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3909 if (newstr)
3911 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3912 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3913 HeapFree( GetProcessHeap(), 0, newstr );
3915 return ret;
3917 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3921 /**********************************************************************
3922 * CreatePopupMenu (USER32.@)
3924 HMENU WINAPI CreatePopupMenu(void)
3926 HMENU hmenu;
3927 POPUPMENU *menu;
3929 if (!(hmenu = CreateMenu())) return 0;
3930 menu = MENU_GetMenu( hmenu );
3931 menu->wFlags |= MF_POPUP;
3932 menu->bTimeToHide = FALSE;
3933 return hmenu;
3937 /**********************************************************************
3938 * GetMenuCheckMarkDimensions (USER.417)
3939 * GetMenuCheckMarkDimensions (USER32.@)
3941 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3943 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3947 /**********************************************************************
3948 * SetMenuItemBitmaps (USER32.@)
3950 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3951 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3953 MENUITEM *item;
3954 TRACE("(%p, %04x, %04x, %p, %p)\n",
3955 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3956 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3958 if (!hNewCheck && !hNewUnCheck)
3960 item->fState &= ~MF_USECHECKBITMAPS;
3962 else /* Install new bitmaps */
3964 item->hCheckBit = hNewCheck;
3965 item->hUnCheckBit = hNewUnCheck;
3966 item->fState |= MF_USECHECKBITMAPS;
3968 return TRUE;
3972 /**********************************************************************
3973 * CreateMenu (USER32.@)
3975 HMENU WINAPI CreateMenu(void)
3977 HMENU hMenu;
3978 LPPOPUPMENU menu;
3979 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3980 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3982 ZeroMemory(menu, sizeof(POPUPMENU));
3983 menu->wMagic = MENU_MAGIC;
3984 menu->FocusedItem = NO_SELECTED_ITEM;
3985 menu->bTimeToHide = FALSE;
3987 TRACE("return %p\n", hMenu );
3989 return hMenu;
3993 /**********************************************************************
3994 * DestroyMenu (USER32.@)
3996 BOOL WINAPI DestroyMenu( HMENU hMenu )
3998 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
4000 TRACE("(%p)\n", hMenu);
4003 if (!lppop) return FALSE;
4005 lppop->wMagic = 0; /* Mark it as destroyed */
4007 /* DestroyMenu should not destroy system menu popup owner */
4008 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4010 DestroyWindow( lppop->hWnd );
4011 lppop->hWnd = 0;
4014 if (lppop->items) /* recursively destroy submenus */
4016 int i;
4017 MENUITEM *item = lppop->items;
4018 for (i = lppop->nItems; i > 0; i--, item++)
4020 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4021 MENU_FreeItemData( item );
4023 HeapFree( GetProcessHeap(), 0, lppop->items );
4025 USER_HEAP_FREE( hMenu );
4026 return TRUE;
4030 /**********************************************************************
4031 * GetSystemMenu (USER32.@)
4033 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4035 WND *wndPtr = WIN_GetPtr( hWnd );
4036 HMENU retvalue = 0;
4038 if (wndPtr == WND_DESKTOP) return 0;
4039 if (wndPtr == WND_OTHER_PROCESS)
4041 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4043 else if (wndPtr)
4045 if (wndPtr->hSysMenu && bRevert)
4047 DestroyMenu(wndPtr->hSysMenu);
4048 wndPtr->hSysMenu = 0;
4051 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4052 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4054 if( wndPtr->hSysMenu )
4056 POPUPMENU *menu;
4057 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4059 /* Store the dummy sysmenu handle to facilitate the refresh */
4060 /* of the close button if the SC_CLOSE item change */
4061 menu = MENU_GetMenu(retvalue);
4062 if ( menu )
4063 menu->hSysMenuOwner = wndPtr->hSysMenu;
4065 WIN_ReleasePtr( wndPtr );
4067 return bRevert ? 0 : retvalue;
4071 /*******************************************************************
4072 * SetSystemMenu (USER32.@)
4074 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4076 WND *wndPtr = WIN_GetPtr( hwnd );
4078 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4080 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4081 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4082 WIN_ReleasePtr( wndPtr );
4083 return TRUE;
4085 return FALSE;
4089 /**********************************************************************
4090 * GetMenu (USER32.@)
4092 HMENU WINAPI GetMenu( HWND hWnd )
4094 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4095 TRACE("for %p returning %p\n", hWnd, retvalue);
4096 return retvalue;
4099 /**********************************************************************
4100 * GetMenuBarInfo (USER32.@)
4102 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4104 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4105 return FALSE;
4108 /**********************************************************************
4109 * MENU_SetMenu
4111 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4112 * SetWindowPos call that would result if SetMenu were called directly.
4114 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4116 TRACE("(%p, %p);\n", hWnd, hMenu);
4118 if (hMenu && !IsMenu(hMenu))
4120 WARN("hMenu %p is not a menu handle\n", hMenu);
4121 return FALSE;
4123 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4124 return FALSE;
4126 hWnd = WIN_GetFullHandle( hWnd );
4127 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4129 if (hMenu != 0)
4131 LPPOPUPMENU lpmenu;
4133 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4135 lpmenu->hWnd = hWnd;
4136 lpmenu->Height = 0; /* Make sure we recalculate the size */
4138 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4139 return TRUE;
4143 /**********************************************************************
4144 * SetMenu (USER32.@)
4146 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4148 if(!MENU_SetMenu(hWnd, hMenu))
4149 return FALSE;
4151 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4152 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4153 return TRUE;
4157 /**********************************************************************
4158 * GetSubMenu (USER32.@)
4160 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4162 MENUITEM * lpmi;
4164 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4165 if (!(lpmi->fType & MF_POPUP)) return 0;
4166 return lpmi->hSubMenu;
4170 /**********************************************************************
4171 * DrawMenuBar (USER32.@)
4173 BOOL WINAPI DrawMenuBar( HWND hWnd )
4175 LPPOPUPMENU lppop;
4176 HMENU hMenu = GetMenu(hWnd);
4178 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4179 return FALSE;
4180 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4182 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4183 lppop->hwndOwner = hWnd;
4184 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4185 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4186 return TRUE;
4189 /***********************************************************************
4190 * DrawMenuBarTemp (USER32.@)
4192 * UNDOCUMENTED !!
4194 * called by W98SE desk.cpl Control Panel Applet
4196 * Not 100% sure about the param names, but close.
4198 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4200 LPPOPUPMENU lppop;
4201 UINT i,retvalue;
4202 HFONT hfontOld = 0;
4203 BOOL flat_menu = FALSE;
4205 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4207 if (!hMenu)
4208 hMenu = GetMenu(hwnd);
4210 if (!hFont)
4211 hFont = get_menu_font(FALSE);
4213 lppop = MENU_GetMenu( hMenu );
4214 if (lppop == NULL || lprect == NULL)
4216 retvalue = GetSystemMetrics(SM_CYMENU);
4217 goto END;
4220 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4222 hfontOld = SelectObject( hDC, hFont);
4224 if (lppop->Height == 0)
4225 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4227 lprect->bottom = lprect->top + lppop->Height;
4229 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4231 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4232 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4233 LineTo( hDC, lprect->right, lprect->bottom );
4235 if (lppop->nItems == 0)
4237 retvalue = GetSystemMetrics(SM_CYMENU);
4238 goto END;
4241 for (i = 0; i < lppop->nItems; i++)
4243 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4244 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4246 retvalue = lppop->Height;
4248 END:
4249 if (hfontOld) SelectObject (hDC, hfontOld);
4250 return retvalue;
4253 /***********************************************************************
4254 * EndMenu (USER.187)
4255 * EndMenu (USER32.@)
4257 void WINAPI EndMenu(void)
4259 /* if we are in the menu code, and it is active */
4260 if (!fEndMenu && top_popup)
4262 /* terminate the menu handling code */
4263 fEndMenu = TRUE;
4265 /* needs to be posted to wakeup the internal menu handler */
4266 /* which will now terminate the menu, in the event that */
4267 /* the main window was minimized, or lost focus, so we */
4268 /* don't end up with an orphaned menu */
4269 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4274 /***********************************************************************
4275 * LookupMenuHandle (USER.217)
4277 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4279 HMENU hmenu32 = HMENU_32(hmenu);
4280 UINT id32 = id;
4281 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4282 else return HMENU_16(hmenu32);
4286 /**********************************************************************
4287 * LoadMenu (USER.150)
4289 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4291 HRSRC16 hRsrc;
4292 HGLOBAL16 handle;
4293 HMENU16 hMenu;
4295 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4296 if (!name) return 0;
4298 instance = GetExePtr( instance );
4299 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4300 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4301 hMenu = LoadMenuIndirect16(LockResource16(handle));
4302 FreeResource16( handle );
4303 return hMenu;
4307 /*****************************************************************
4308 * LoadMenuA (USER32.@)
4310 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4312 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4313 if (!hrsrc) return 0;
4314 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4318 /*****************************************************************
4319 * LoadMenuW (USER32.@)
4321 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4323 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4324 if (!hrsrc) return 0;
4325 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4329 /**********************************************************************
4330 * LoadMenuIndirect (USER.220)
4332 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4334 HMENU hMenu;
4335 WORD version, offset;
4336 LPCSTR p = (LPCSTR)template;
4338 TRACE("(%p)\n", template );
4339 version = GET_WORD(p);
4340 p += sizeof(WORD);
4341 if (version)
4343 WARN("version must be 0 for Win16\n" );
4344 return 0;
4346 offset = GET_WORD(p);
4347 p += sizeof(WORD) + offset;
4348 if (!(hMenu = CreateMenu())) return 0;
4349 if (!MENU_ParseResource( p, hMenu, FALSE ))
4351 DestroyMenu( hMenu );
4352 return 0;
4354 return HMENU_16(hMenu);
4358 /**********************************************************************
4359 * LoadMenuIndirectW (USER32.@)
4361 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4363 HMENU hMenu;
4364 WORD version, offset;
4365 LPCSTR p = (LPCSTR)template;
4367 version = GET_WORD(p);
4368 p += sizeof(WORD);
4369 TRACE("%p, ver %d\n", template, version );
4370 switch (version)
4372 case 0: /* standard format is version of 0 */
4373 offset = GET_WORD(p);
4374 p += sizeof(WORD) + offset;
4375 if (!(hMenu = CreateMenu())) return 0;
4376 if (!MENU_ParseResource( p, hMenu, TRUE ))
4378 DestroyMenu( hMenu );
4379 return 0;
4381 return hMenu;
4382 case 1: /* extended format is version of 1 */
4383 offset = GET_WORD(p);
4384 p += sizeof(WORD) + offset;
4385 if (!(hMenu = CreateMenu())) return 0;
4386 if (!MENUEX_ParseResource( p, hMenu))
4388 DestroyMenu( hMenu );
4389 return 0;
4391 return hMenu;
4392 default:
4393 ERR("version %d not supported.\n", version);
4394 return 0;
4399 /**********************************************************************
4400 * LoadMenuIndirectA (USER32.@)
4402 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4404 return LoadMenuIndirectW( template );
4408 /**********************************************************************
4409 * IsMenu (USER32.@)
4411 BOOL WINAPI IsMenu(HMENU hmenu)
4413 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4415 if (!menu)
4417 SetLastError(ERROR_INVALID_MENU_HANDLE);
4418 return FALSE;
4420 return TRUE;
4423 /**********************************************************************
4424 * GetMenuItemInfo_common
4427 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4428 LPMENUITEMINFOW lpmii, BOOL unicode)
4430 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4432 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4434 if (!menu)
4435 return FALSE;
4437 if( lpmii->fMask & MIIM_TYPE) {
4438 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4439 WARN("invalid combination of fMask bits used\n");
4440 /* this does not happen on Win9x/ME */
4441 SetLastError( ERROR_INVALID_PARAMETER);
4442 return FALSE;
4444 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4445 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4446 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4447 if( lpmii->fType & MFT_BITMAP) {
4448 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4449 lpmii->cch = 0;
4450 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4451 /* this does not happen on Win9x/ME */
4452 lpmii->dwTypeData = 0;
4453 lpmii->cch = 0;
4457 /* copy the text string */
4458 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4459 if( !menu->text ) {
4460 if(lpmii->dwTypeData && lpmii->cch) {
4461 lpmii->cch = 0;
4462 if( unicode)
4463 *((WCHAR *)lpmii->dwTypeData) = 0;
4464 else
4465 *((CHAR *)lpmii->dwTypeData) = 0;
4467 } else {
4468 int len;
4469 if (unicode)
4471 len = strlenW(menu->text);
4472 if(lpmii->dwTypeData && lpmii->cch)
4473 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4475 else
4477 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4478 0, NULL, NULL ) - 1;
4479 if(lpmii->dwTypeData && lpmii->cch)
4480 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4481 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4482 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4484 /* if we've copied a substring we return its length */
4485 if(lpmii->dwTypeData && lpmii->cch)
4486 if (lpmii->cch <= len + 1)
4487 lpmii->cch--;
4488 else
4489 lpmii->cch = len;
4490 else {
4491 /* return length of string */
4492 /* not on Win9x/ME if fType & MFT_BITMAP */
4493 lpmii->cch = len;
4498 if (lpmii->fMask & MIIM_FTYPE)
4499 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4501 if (lpmii->fMask & MIIM_BITMAP)
4502 lpmii->hbmpItem = menu->hbmpItem;
4504 if (lpmii->fMask & MIIM_STATE)
4505 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4507 if (lpmii->fMask & MIIM_ID)
4508 lpmii->wID = menu->wID;
4510 if (lpmii->fMask & MIIM_SUBMENU)
4511 lpmii->hSubMenu = menu->hSubMenu;
4512 else {
4513 /* hSubMenu is always cleared
4514 * (not on Win9x/ME ) */
4515 lpmii->hSubMenu = 0;
4518 if (lpmii->fMask & MIIM_CHECKMARKS) {
4519 lpmii->hbmpChecked = menu->hCheckBit;
4520 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4522 if (lpmii->fMask & MIIM_DATA)
4523 lpmii->dwItemData = menu->dwItemData;
4525 return TRUE;
4528 /**********************************************************************
4529 * GetMenuItemInfoA (USER32.@)
4531 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4532 LPMENUITEMINFOA lpmii)
4534 BOOL ret;
4535 MENUITEMINFOA mii;
4536 if( lpmii->cbSize != sizeof( mii) &&
4537 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4538 SetLastError( ERROR_INVALID_PARAMETER);
4539 return FALSE;
4541 memcpy( &mii, lpmii, lpmii->cbSize);
4542 mii.cbSize = sizeof( mii);
4543 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4544 (LPMENUITEMINFOW)&mii, FALSE);
4545 mii.cbSize = lpmii->cbSize;
4546 memcpy( lpmii, &mii, mii.cbSize);
4547 return ret;
4550 /**********************************************************************
4551 * GetMenuItemInfoW (USER32.@)
4553 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4554 LPMENUITEMINFOW lpmii)
4556 BOOL ret;
4557 MENUITEMINFOW mii;
4558 if( lpmii->cbSize != sizeof( mii) &&
4559 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4560 SetLastError( ERROR_INVALID_PARAMETER);
4561 return FALSE;
4563 memcpy( &mii, lpmii, lpmii->cbSize);
4564 mii.cbSize = sizeof( mii);
4565 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4566 mii.cbSize = lpmii->cbSize;
4567 memcpy( lpmii, &mii, mii.cbSize);
4568 return ret;
4572 /* set a menu item text from a ASCII or Unicode string */
4573 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4575 if (!text)
4576 menu->text = NULL;
4577 else if (unicode)
4579 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4580 strcpyW( menu->text, text );
4582 else
4584 LPCSTR str = (LPCSTR)text;
4585 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4586 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4587 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4592 /**********************************************************************
4593 * SetMenuItemInfo_common
4596 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4597 const MENUITEMINFOW *lpmii,
4598 BOOL unicode)
4600 if (!menu) return FALSE;
4602 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4604 if (lpmii->fMask & MIIM_TYPE ) {
4605 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4606 WARN("invalid combination of fMask bits used\n");
4607 /* this does not happen on Win9x/ME */
4608 SetLastError( ERROR_INVALID_PARAMETER);
4609 return FALSE;
4612 /* Remove the old type bits and replace them with the new ones */
4613 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4614 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4616 if (IS_STRING_ITEM(menu->fType)) {
4617 HeapFree(GetProcessHeap(), 0, menu->text);
4618 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4619 } else if( (menu->fType) & MFT_BITMAP)
4620 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4623 if (lpmii->fMask & MIIM_FTYPE ) {
4624 if(( lpmii->fType & MFT_BITMAP)) {
4625 SetLastError( ERROR_INVALID_PARAMETER);
4626 return FALSE;
4628 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4629 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4631 if (lpmii->fMask & MIIM_STRING ) {
4632 /* free the string when used */
4633 HeapFree(GetProcessHeap(), 0, menu->text);
4634 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4637 if (lpmii->fMask & MIIM_STATE)
4639 /* Other menu items having MFS_DEFAULT are not converted
4640 to normal items */
4641 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4644 if (lpmii->fMask & MIIM_ID)
4645 menu->wID = lpmii->wID;
4647 if (lpmii->fMask & MIIM_SUBMENU) {
4648 menu->hSubMenu = lpmii->hSubMenu;
4649 if (menu->hSubMenu) {
4650 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4651 if (subMenu) {
4652 subMenu->wFlags |= MF_POPUP;
4653 menu->fType |= MF_POPUP;
4655 else {
4656 SetLastError( ERROR_INVALID_PARAMETER);
4657 return FALSE;
4660 else
4661 menu->fType &= ~MF_POPUP;
4664 if (lpmii->fMask & MIIM_CHECKMARKS)
4666 menu->hCheckBit = lpmii->hbmpChecked;
4667 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4669 if (lpmii->fMask & MIIM_DATA)
4670 menu->dwItemData = lpmii->dwItemData;
4672 if (lpmii->fMask & MIIM_BITMAP)
4673 menu->hbmpItem = lpmii->hbmpItem;
4675 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4676 menu->fType |= MFT_SEPARATOR;
4678 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4679 return TRUE;
4682 /**********************************************************************
4683 * SetMenuItemInfoA (USER32.@)
4685 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4686 const MENUITEMINFOA *lpmii)
4688 MENUITEMINFOA mii;
4690 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4692 if( lpmii->cbSize != sizeof( mii) &&
4693 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4694 SetLastError( ERROR_INVALID_PARAMETER);
4695 return FALSE;
4697 memcpy( &mii, lpmii, lpmii->cbSize);
4698 if( lpmii->cbSize != sizeof( mii)) {
4699 mii.cbSize = sizeof( mii);
4700 mii.hbmpItem = NULL;
4702 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4703 (const MENUITEMINFOW *)&mii, FALSE);
4706 /**********************************************************************
4707 * SetMenuItemInfoW (USER32.@)
4709 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4710 const MENUITEMINFOW *lpmii)
4712 MENUITEMINFOW mii;
4714 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4716 if( lpmii->cbSize != sizeof( mii) &&
4717 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4718 SetLastError( ERROR_INVALID_PARAMETER);
4719 return FALSE;
4721 memcpy( &mii, lpmii, lpmii->cbSize);
4722 if( lpmii->cbSize != sizeof( mii)) {
4723 mii.cbSize = sizeof( mii);
4724 mii.hbmpItem = NULL;
4726 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4727 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4730 /**********************************************************************
4731 * SetMenuDefaultItem (USER32.@)
4734 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4736 UINT i;
4737 POPUPMENU *menu;
4738 MENUITEM *item;
4740 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4742 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4744 /* reset all default-item flags */
4745 item = menu->items;
4746 for (i = 0; i < menu->nItems; i++, item++)
4748 item->fState &= ~MFS_DEFAULT;
4751 /* no default item */
4752 if ( -1 == uItem)
4754 return TRUE;
4757 item = menu->items;
4758 if ( bypos )
4760 if ( uItem >= menu->nItems ) return FALSE;
4761 item[uItem].fState |= MFS_DEFAULT;
4762 return TRUE;
4764 else
4766 for (i = 0; i < menu->nItems; i++, item++)
4768 if (item->wID == uItem)
4770 item->fState |= MFS_DEFAULT;
4771 return TRUE;
4776 return FALSE;
4779 /**********************************************************************
4780 * GetMenuDefaultItem (USER32.@)
4782 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4784 POPUPMENU *menu;
4785 MENUITEM * item;
4786 UINT i = 0;
4788 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4790 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4792 /* find default item */
4793 item = menu->items;
4795 /* empty menu */
4796 if (! item) return -1;
4798 while ( !( item->fState & MFS_DEFAULT ) )
4800 i++; item++;
4801 if (i >= menu->nItems ) return -1;
4804 /* default: don't return disabled items */
4805 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4807 /* search rekursiv when needed */
4808 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4810 UINT ret;
4811 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4812 if ( -1 != ret ) return ret;
4814 /* when item not found in submenu, return the popup item */
4816 return ( bypos ) ? i : item->wID;
4821 /**********************************************************************
4822 * InsertMenuItemA (USER32.@)
4824 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4825 const MENUITEMINFOA *lpmii)
4827 MENUITEM *item;
4828 MENUITEMINFOA mii;
4830 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4832 if( lpmii->cbSize != sizeof( mii) &&
4833 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4834 SetLastError( ERROR_INVALID_PARAMETER);
4835 return FALSE;
4837 memcpy( &mii, lpmii, lpmii->cbSize);
4838 if( lpmii->cbSize != sizeof( mii)) {
4839 mii.cbSize = sizeof( mii);
4840 mii.hbmpItem = NULL;
4843 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4844 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4848 /**********************************************************************
4849 * InsertMenuItemW (USER32.@)
4851 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4852 const MENUITEMINFOW *lpmii)
4854 MENUITEM *item;
4855 MENUITEMINFOW mii;
4857 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4859 if( lpmii->cbSize != sizeof( mii) &&
4860 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4861 SetLastError( ERROR_INVALID_PARAMETER);
4862 return FALSE;
4864 memcpy( &mii, lpmii, lpmii->cbSize);
4865 if( lpmii->cbSize != sizeof( mii)) {
4866 mii.cbSize = sizeof( mii);
4867 mii.hbmpItem = NULL;
4870 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4871 return SetMenuItemInfo_common(item, &mii, TRUE);
4874 /**********************************************************************
4875 * CheckMenuRadioItem (USER32.@)
4878 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4879 UINT first, UINT last, UINT check,
4880 UINT bypos)
4882 BOOL done = FALSE;
4883 UINT i;
4884 MENUITEM *mi_first = NULL, *mi_check;
4885 HMENU m_first, m_check;
4887 TRACE("%p: %u-%u, check %u, flags %04x\n", hMenu, first, last, check, bypos);
4889 for (i = first; i <= last; i++)
4891 UINT pos = i;
4893 if (!mi_first)
4895 m_first = hMenu;
4896 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4897 if (!mi_first) continue;
4898 mi_check = mi_first;
4899 m_check = m_first;
4901 else
4903 m_check = hMenu;
4904 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4905 if (!mi_check) continue;
4908 if (m_first != m_check) continue;
4909 if (mi_check->fType == MFT_SEPARATOR) continue;
4911 if (i == check)
4913 mi_check->fType |= MFT_RADIOCHECK;
4914 mi_check->fState |= MFS_CHECKED;
4915 done = TRUE;
4917 else
4919 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4920 mi_check->fState &= ~MFS_CHECKED;
4924 return done;
4928 /**********************************************************************
4929 * GetMenuItemRect (USER32.@)
4931 * ATTENTION: Here, the returned values in rect are the screen
4932 * coordinates of the item just like if the menu was
4933 * always on the upper left side of the application.
4936 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4937 LPRECT rect)
4939 POPUPMENU *itemMenu;
4940 MENUITEM *item;
4941 HWND referenceHwnd;
4943 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4945 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4946 referenceHwnd = hwnd;
4948 if(!hwnd)
4950 itemMenu = MENU_GetMenu(hMenu);
4951 if (itemMenu == NULL)
4952 return FALSE;
4954 if(itemMenu->hWnd == 0)
4955 return FALSE;
4956 referenceHwnd = itemMenu->hWnd;
4959 if ((rect == NULL) || (item == NULL))
4960 return FALSE;
4962 *rect = item->rect;
4964 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4966 return TRUE;
4970 /**********************************************************************
4971 * SetMenuInfo (USER32.@)
4973 * FIXME
4974 * MIM_APPLYTOSUBMENUS
4975 * actually use the items to draw the menu
4977 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4979 POPUPMENU *menu;
4981 TRACE("(%p %p)\n", hMenu, lpmi);
4983 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4986 if (lpmi->fMask & MIM_BACKGROUND)
4987 menu->hbrBack = lpmi->hbrBack;
4989 if (lpmi->fMask & MIM_HELPID)
4990 menu->dwContextHelpID = lpmi->dwContextHelpID;
4992 if (lpmi->fMask & MIM_MAXHEIGHT)
4993 menu->cyMax = lpmi->cyMax;
4995 if (lpmi->fMask & MIM_MENUDATA)
4996 menu->dwMenuData = lpmi->dwMenuData;
4998 if (lpmi->fMask & MIM_STYLE)
5000 menu->dwStyle = lpmi->dwStyle;
5001 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5002 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5003 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5004 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
5007 return TRUE;
5009 return FALSE;
5012 /**********************************************************************
5013 * GetMenuInfo (USER32.@)
5015 * NOTES
5016 * win98/NT5.0
5019 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5020 { POPUPMENU *menu;
5022 TRACE("(%p %p)\n", hMenu, lpmi);
5024 if (lpmi && (menu = MENU_GetMenu(hMenu)))
5027 if (lpmi->fMask & MIM_BACKGROUND)
5028 lpmi->hbrBack = menu->hbrBack;
5030 if (lpmi->fMask & MIM_HELPID)
5031 lpmi->dwContextHelpID = menu->dwContextHelpID;
5033 if (lpmi->fMask & MIM_MAXHEIGHT)
5034 lpmi->cyMax = menu->cyMax;
5036 if (lpmi->fMask & MIM_MENUDATA)
5037 lpmi->dwMenuData = menu->dwMenuData;
5039 if (lpmi->fMask & MIM_STYLE)
5040 lpmi->dwStyle = menu->dwStyle;
5042 return TRUE;
5044 return FALSE;
5048 /**********************************************************************
5049 * SetMenuContextHelpId (USER32.@)
5051 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5053 LPPOPUPMENU menu;
5055 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5057 if ((menu = MENU_GetMenu(hMenu)))
5059 menu->dwContextHelpID = dwContextHelpID;
5060 return TRUE;
5062 return FALSE;
5066 /**********************************************************************
5067 * GetMenuContextHelpId (USER32.@)
5069 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5071 LPPOPUPMENU menu;
5073 TRACE("(%p)\n", hMenu);
5075 if ((menu = MENU_GetMenu(hMenu)))
5077 return menu->dwContextHelpID;
5079 return 0;
5082 /**********************************************************************
5083 * MenuItemFromPoint (USER32.@)
5085 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5087 POPUPMENU *menu = MENU_GetMenu(hMenu);
5088 UINT pos;
5090 /*FIXME: Do we have to handle hWnd here? */
5091 if (!menu) return -1;
5092 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5093 return pos;
5097 /**********************************************************************
5098 * translate_accelerator
5100 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5101 BYTE fVirt, WORD key, WORD cmd )
5103 INT mask = 0;
5104 UINT mesg = 0;
5106 if (wParam != key) return FALSE;
5108 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5109 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5110 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5112 if (message == WM_CHAR || message == WM_SYSCHAR)
5114 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5116 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5117 goto found;
5120 else
5122 if(fVirt & FVIRTKEY)
5124 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5125 wParam, 0xff & HIWORD(lParam));
5127 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5128 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5130 else
5132 if (!(lParam & 0x01000000)) /* no special_key */
5134 if ((fVirt & FALT) && (lParam & 0x20000000))
5135 { /* ^^ ALT pressed */
5136 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5137 goto found;
5142 return FALSE;
5144 found:
5145 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5146 mesg = 1;
5147 else
5149 HMENU hMenu, hSubMenu, hSysMenu;
5150 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5152 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5153 hSysMenu = get_win_sys_menu( hWnd );
5155 /* find menu item and ask application to initialize it */
5156 /* 1. in the system menu */
5157 hSubMenu = hSysMenu;
5158 nPos = cmd;
5159 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5161 if (GetCapture())
5162 mesg = 2;
5163 if (!IsWindowEnabled(hWnd))
5164 mesg = 3;
5165 else
5167 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5168 if(hSubMenu != hSysMenu)
5170 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5171 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5172 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5174 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5177 else /* 2. in the window's menu */
5179 hSubMenu = hMenu;
5180 nPos = cmd;
5181 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5183 if (GetCapture())
5184 mesg = 2;
5185 if (!IsWindowEnabled(hWnd))
5186 mesg = 3;
5187 else
5189 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5190 if(hSubMenu != hMenu)
5192 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5193 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5194 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5196 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5201 if (mesg == 0)
5203 if (uSysStat != (UINT)-1)
5205 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5206 mesg=4;
5207 else
5208 mesg=WM_SYSCOMMAND;
5210 else
5212 if (uStat != (UINT)-1)
5214 if (IsIconic(hWnd))
5215 mesg=5;
5216 else
5218 if (uStat & (MF_DISABLED|MF_GRAYED))
5219 mesg=6;
5220 else
5221 mesg=WM_COMMAND;
5224 else
5225 mesg=WM_COMMAND;
5230 if( mesg==WM_COMMAND )
5232 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5233 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5235 else if( mesg==WM_SYSCOMMAND )
5237 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5238 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5240 else
5242 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5243 * #0: unknown (please report!)
5244 * #1: for WM_KEYUP,WM_SYSKEYUP
5245 * #2: mouse is captured
5246 * #3: window is disabled
5247 * #4: it's a disabled system menu option
5248 * #5: it's a menu option, but window is iconic
5249 * #6: it's a menu option, but disabled
5251 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5252 if(mesg==0)
5253 ERR_(accel)(" unknown reason - please report!\n");
5255 return TRUE;
5258 /**********************************************************************
5259 * TranslateAcceleratorA (USER32.@)
5260 * TranslateAccelerator (USER32.@)
5262 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5264 /* YES, Accel16! */
5265 LPACCEL16 lpAccelTbl;
5266 int i;
5267 WPARAM wParam;
5269 if (!hWnd || !msg) return 0;
5271 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5273 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5274 return 0;
5277 wParam = msg->wParam;
5279 switch (msg->message)
5281 case WM_KEYDOWN:
5282 case WM_SYSKEYDOWN:
5283 break;
5285 case WM_CHAR:
5286 case WM_SYSCHAR:
5288 char ch = LOWORD(wParam);
5289 WCHAR wch;
5290 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5291 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5293 break;
5295 default:
5296 return 0;
5299 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5300 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5301 i = 0;
5304 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5305 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5306 return 1;
5307 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5309 return 0;
5312 /**********************************************************************
5313 * TranslateAcceleratorW (USER32.@)
5315 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5317 /* YES, Accel16! */
5318 LPACCEL16 lpAccelTbl;
5319 int i;
5321 if (!hWnd || !msg) return 0;
5323 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5325 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5326 return 0;
5329 switch (msg->message)
5331 case WM_KEYDOWN:
5332 case WM_SYSKEYDOWN:
5333 case WM_CHAR:
5334 case WM_SYSCHAR:
5335 break;
5337 default:
5338 return 0;
5341 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5342 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5343 i = 0;
5346 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5347 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5348 return 1;
5349 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5351 return 0;