msvcrt/tests: Remove a space before a '\n'.
[wine/gsoc-2012-control.git] / dlls / user32 / menu.c
blobefb14c6ba68ca5b87e4f030279b81c016a0e0067
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 "wine/exception.h"
60 #include "win.h"
61 #include "controls.h"
62 #include "user_private.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(menu);
66 WINE_DECLARE_DEBUG_CHANNEL(accel);
68 /* internal popup menu window messages */
70 #define MM_SETMENUHANDLE (WM_USER + 0)
71 #define MM_GETMENUHANDLE (WM_USER + 1)
73 /* Menu item structure */
74 typedef struct {
75 /* ----------- MENUITEMINFO Stuff ----------- */
76 UINT fType; /* Item type. */
77 UINT fState; /* Item state. */
78 UINT_PTR wID; /* Item id. */
79 HMENU hSubMenu; /* Pop-up menu. */
80 HBITMAP hCheckBit; /* Bitmap when checked. */
81 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
82 LPWSTR text; /* Item text. */
83 ULONG_PTR dwItemData; /* Application defined. */
84 LPWSTR dwTypeData; /* depends on fMask */
85 HBITMAP hbmpItem; /* bitmap */
86 /* ----------- Wine stuff ----------- */
87 RECT rect; /* Item area (relative to menu window) */
88 UINT xTab; /* X position of text after Tab */
89 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
90 * bitmap */
91 } MENUITEM;
93 /* Popup menu structure */
94 typedef struct {
95 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
96 WORD wMagic; /* Magic number */
97 WORD Width; /* Width of the whole menu */
98 WORD Height; /* Height of the whole menu */
99 UINT nItems; /* Number of items in the menu */
100 HWND hWnd; /* Window containing the menu */
101 MENUITEM *items; /* Array of menu items */
102 UINT FocusedItem; /* Currently focused item */
103 HWND hwndOwner; /* window receiving the messages for ownerdraw */
104 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
105 BOOL bScrolling; /* Scroll arrows are active */
106 UINT nScrollPos; /* Current scroll position */
107 UINT nTotalHeight; /* Total height of menu items inside menu */
108 /* ------------ MENUINFO members ------ */
109 DWORD dwStyle; /* Extended menu style */
110 UINT cyMax; /* max height of the whole menu, 0 is screen height */
111 HBRUSH hbrBack; /* brush for menu background */
112 DWORD dwContextHelpID;
113 DWORD dwMenuData; /* application defined value */
114 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
115 SIZE maxBmpSize; /* Maximum size of the bitmap items */
116 } POPUPMENU, *LPPOPUPMENU;
118 /* internal flags for menu tracking */
120 #define TF_ENDMENU 0x10000
121 #define TF_SUSPENDPOPUP 0x20000
122 #define TF_SKIPREMOVE 0x40000
124 typedef struct
126 UINT trackFlags;
127 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
128 HMENU hTopMenu; /* initial menu */
129 HWND hOwnerWnd; /* where notifications are sent */
130 POINT pt;
131 } MTRACKER;
133 #define MENU_MAGIC 0x554d /* 'MU' */
135 #define ITEM_PREV -1
136 #define ITEM_NEXT 1
138 /* Internal MENU_TrackMenu() flags */
139 #define TPM_INTERNAL 0xF0000000
140 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
141 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
142 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
144 /* Space between 2 columns */
145 #define MENU_COL_SPACE 4
147 /* top and bottom margins for popup menus */
148 #define MENU_TOP_MARGIN 3
149 #define MENU_BOTTOM_MARGIN 2
151 /* (other menu->FocusedItem values give the position of the focused item) */
152 #define NO_SELECTED_ITEM 0xffff
154 #define MENU_ITEM_TYPE(flags) \
155 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
157 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
158 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
159 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
161 #define IS_SYSTEM_MENU(menu) \
162 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
164 #define MENUITEMINFO_TYPE_MASK \
165 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
166 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
167 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
168 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
169 #define STATE_MASK (~TYPE_MASK)
170 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
172 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
174 static SIZE menucharsize;
175 static UINT ODitemheight; /* default owner drawn item height */
177 /* Use global popup window because there's no way 2 menus can
178 * be tracked at the same time. */
179 static HWND top_popup;
181 /* Flag set by EndMenu() to force an exit from menu tracking */
182 static BOOL fEndMenu = FALSE;
184 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
186 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
188 /*********************************************************************
189 * menu class descriptor
191 const struct builtin_class_descr MENU_builtin_class =
193 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
194 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
195 NULL, /* procA (winproc is Unicode only) */
196 PopupMenuWndProc, /* procW */
197 sizeof(HMENU), /* extra */
198 IDC_ARROW, /* cursor */
199 (HBRUSH)(COLOR_MENU+1) /* brush */
203 /***********************************************************************
204 * debug_print_menuitem
206 * Print a menuitem in readable form.
209 #define debug_print_menuitem(pre, mp, post) \
210 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
212 #define MENUOUT(text) \
213 TRACE("%s%s", (count++ ? "," : ""), (text))
215 #define MENUFLAG(bit,text) \
216 do { \
217 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
218 } while (0)
220 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
221 const char *postfix)
223 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
224 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
225 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
226 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
227 TRACE("%s ", prefix);
228 if (mp) {
229 UINT flags = mp->fType;
230 TRACE( "{ ID=0x%lx", mp->wID);
231 if ( mp->hSubMenu)
232 TRACE( ", Sub=%p", mp->hSubMenu);
233 if (flags) {
234 int count = 0;
235 TRACE( ", fType=");
236 MENUFLAG( MFT_SEPARATOR, "sep");
237 MENUFLAG( MFT_OWNERDRAW, "own");
238 MENUFLAG( MFT_BITMAP, "bit");
239 MENUFLAG(MF_POPUP, "pop");
240 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
241 MENUFLAG(MFT_MENUBREAK, "brk");
242 MENUFLAG(MFT_RADIOCHECK, "radio");
243 MENUFLAG(MFT_RIGHTORDER, "rorder");
244 MENUFLAG(MF_SYSMENU, "sys");
245 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
246 if (flags)
247 TRACE( "+0x%x", flags);
249 flags = mp->fState;
250 if (flags) {
251 int count = 0;
252 TRACE( ", State=");
253 MENUFLAG(MFS_GRAYED, "grey");
254 MENUFLAG(MFS_DEFAULT, "default");
255 MENUFLAG(MFS_DISABLED, "dis");
256 MENUFLAG(MFS_CHECKED, "check");
257 MENUFLAG(MFS_HILITE, "hi");
258 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
259 MENUFLAG(MF_MOUSESELECT, "mouse");
260 if (flags)
261 TRACE( "+0x%x", flags);
263 if (mp->hCheckBit)
264 TRACE( ", Chk=%p", mp->hCheckBit);
265 if (mp->hUnCheckBit)
266 TRACE( ", Unc=%p", mp->hUnCheckBit);
267 if (mp->text)
268 TRACE( ", Text=%s", debugstr_w(mp->text));
269 if (mp->dwItemData)
270 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
271 if (mp->hbmpItem)
273 if( IS_MAGIC_BITMAP(mp->hbmpItem))
274 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
275 else
276 TRACE( ", hbitmap=%p", mp->hbmpItem);
278 TRACE( " }");
279 } else
280 TRACE( "NULL");
281 TRACE(" %s\n", postfix);
284 #undef MENUOUT
285 #undef MENUFLAG
288 /***********************************************************************
289 * MENU_GetMenu
291 * Validate the given menu handle and returns the menu structure pointer.
293 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
295 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
296 if (!menu || menu->wMagic != MENU_MAGIC)
298 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
299 menu = NULL;
301 return menu;
304 /***********************************************************************
305 * get_win_sys_menu
307 * Get the system menu of a window
309 static HMENU get_win_sys_menu( HWND hwnd )
311 HMENU ret = 0;
312 WND *win = WIN_GetPtr( hwnd );
313 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
315 ret = win->hSysMenu;
316 WIN_ReleasePtr( win );
318 return ret;
321 /***********************************************************************
322 * get_menu_font
324 static HFONT get_menu_font( BOOL bold )
326 static HFONT hMenuFont, hMenuFontBold;
328 HFONT ret = bold ? hMenuFontBold : hMenuFont;
330 if (!ret)
332 NONCLIENTMETRICSW ncm;
333 HFONT prev;
335 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
336 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
338 if (bold)
340 ncm.lfMenuFont.lfWeight += 300;
341 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
343 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
344 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
345 ret, NULL );
346 if (prev)
348 /* another thread beat us to it */
349 DeleteObject( ret );
350 ret = prev;
353 return ret;
356 /***********************************************************************
357 * get_arrow_bitmap
359 static HBITMAP get_arrow_bitmap(void)
361 static HBITMAP arrow_bitmap;
363 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
364 return arrow_bitmap;
367 /***********************************************************************
368 * get_down_arrow_bitmap
370 static HBITMAP get_down_arrow_bitmap(void)
372 static HBITMAP arrow_bitmap;
374 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
375 return arrow_bitmap;
378 /***********************************************************************
379 * get_down_arrow_inactive_bitmap
381 static HBITMAP get_down_arrow_inactive_bitmap(void)
383 static HBITMAP arrow_bitmap;
385 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
386 return arrow_bitmap;
389 /***********************************************************************
390 * get_up_arrow_bitmap
392 static HBITMAP get_up_arrow_bitmap(void)
394 static HBITMAP arrow_bitmap;
396 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
397 return arrow_bitmap;
400 /***********************************************************************
401 * get_up_arrow_inactive_bitmap
403 static HBITMAP get_up_arrow_inactive_bitmap(void)
405 static HBITMAP arrow_bitmap;
407 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
408 return arrow_bitmap;
411 /***********************************************************************
412 * MENU_CopySysPopup
414 * Return the default system menu.
416 static HMENU MENU_CopySysPopup(void)
418 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
419 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
421 if( hMenu ) {
422 POPUPMENU* menu = MENU_GetMenu(hMenu);
423 menu->wFlags |= MF_SYSMENU | MF_POPUP;
424 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
426 else
427 ERR("Unable to load default system menu\n" );
429 TRACE("returning %p.\n", hMenu );
431 return hMenu;
435 /**********************************************************************
436 * MENU_GetSysMenu
438 * Create a copy of the system menu. System menu in Windows is
439 * a special menu bar with the single entry - system menu popup.
440 * This popup is presented to the outside world as a "system menu".
441 * However, the real system menu handle is sometimes seen in the
442 * WM_MENUSELECT parameters (and Word 6 likes it this way).
444 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
446 HMENU hMenu;
448 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
449 if ((hMenu = CreateMenu()))
451 POPUPMENU *menu = MENU_GetMenu(hMenu);
452 menu->wFlags = MF_SYSMENU;
453 menu->hWnd = WIN_GetFullHandle( hWnd );
454 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
456 if (!hPopupMenu)
457 hPopupMenu = MENU_CopySysPopup();
459 if (hPopupMenu)
461 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
462 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
464 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
465 (UINT_PTR)hPopupMenu, NULL );
467 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
468 menu->items[0].fState = 0;
469 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
471 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
472 return hMenu;
474 DestroyMenu( hMenu );
476 ERR("failed to load system menu!\n");
477 return 0;
481 /***********************************************************************
482 * MENU_InitSysMenuPopup
484 * Grey the appropriate items in System menu.
486 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
488 BOOL gray;
490 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
491 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
492 gray = ((style & WS_MAXIMIZE) != 0);
493 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
494 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
495 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
496 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
497 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
498 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
499 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
500 gray = (clsStyle & CS_NOCLOSE) != 0;
502 /* The menu item must keep its state if it's disabled */
503 if(gray)
504 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
508 /******************************************************************************
510 * UINT MENU_GetStartOfNextColumn(
511 * HMENU hMenu )
513 *****************************************************************************/
515 static UINT MENU_GetStartOfNextColumn(
516 HMENU hMenu )
518 POPUPMENU *menu = MENU_GetMenu(hMenu);
519 UINT i;
521 if(!menu)
522 return NO_SELECTED_ITEM;
524 i = menu->FocusedItem + 1;
525 if( i == NO_SELECTED_ITEM )
526 return i;
528 for( ; i < menu->nItems; ++i ) {
529 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
530 return i;
533 return NO_SELECTED_ITEM;
537 /******************************************************************************
539 * UINT MENU_GetStartOfPrevColumn(
540 * HMENU hMenu )
542 *****************************************************************************/
544 static UINT MENU_GetStartOfPrevColumn(
545 HMENU hMenu )
547 POPUPMENU *menu = MENU_GetMenu(hMenu);
548 UINT i;
550 if( !menu )
551 return NO_SELECTED_ITEM;
553 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
554 return NO_SELECTED_ITEM;
556 /* Find the start of the column */
558 for(i = menu->FocusedItem; i != 0 &&
559 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
560 --i); /* empty */
562 if(i == 0)
563 return NO_SELECTED_ITEM;
565 for(--i; i != 0; --i) {
566 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
567 break;
570 TRACE("ret %d.\n", i );
572 return i;
577 /***********************************************************************
578 * MENU_FindItem
580 * Find a menu item. Return a pointer on the item, and modifies *hmenu
581 * in case the item was in a sub-menu.
583 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
585 POPUPMENU *menu;
586 MENUITEM *fallback = NULL;
587 UINT fallback_pos = 0;
588 UINT i;
590 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
591 if (wFlags & MF_BYPOSITION)
593 if (*nPos >= menu->nItems) return NULL;
594 return &menu->items[*nPos];
596 else
598 MENUITEM *item = menu->items;
599 for (i = 0; i < menu->nItems; i++, item++)
601 if (item->fType & MF_POPUP)
603 HMENU hsubmenu = item->hSubMenu;
604 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
605 if (subitem)
607 *hmenu = hsubmenu;
608 return subitem;
610 else if (item->wID == *nPos)
612 /* fallback to this item if nothing else found */
613 fallback_pos = i;
614 fallback = item;
617 else if (item->wID == *nPos)
619 *nPos = i;
620 return item;
625 if (fallback)
626 *nPos = fallback_pos;
628 return fallback;
631 /***********************************************************************
632 * MENU_FindSubMenu
634 * Find a Sub menu. Return the position of the submenu, and modifies
635 * *hmenu in case it is found in another sub-menu.
636 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
638 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
640 POPUPMENU *menu;
641 UINT i;
642 MENUITEM *item;
643 if (((*hmenu)==(HMENU)0xffff) ||
644 (!(menu = MENU_GetMenu(*hmenu))))
645 return NO_SELECTED_ITEM;
646 item = menu->items;
647 for (i = 0; i < menu->nItems; i++, item++) {
648 if(!(item->fType & MF_POPUP)) continue;
649 if (item->hSubMenu == hSubTarget) {
650 return i;
652 else {
653 HMENU hsubmenu = item->hSubMenu;
654 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
655 if (pos != NO_SELECTED_ITEM) {
656 *hmenu = hsubmenu;
657 return pos;
661 return NO_SELECTED_ITEM;
664 /***********************************************************************
665 * MENU_FreeItemData
667 static void MENU_FreeItemData( MENUITEM* item )
669 /* delete text */
670 HeapFree( GetProcessHeap(), 0, item->text );
673 /***********************************************************************
674 * MENU_AdjustMenuItemRect
676 * Adjust menu item rectangle according to scrolling state.
678 static void
679 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
681 if (menu->bScrolling)
683 UINT arrow_bitmap_height;
684 BITMAP bmp;
686 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
687 arrow_bitmap_height = bmp.bmHeight;
688 rect->top += arrow_bitmap_height - menu->nScrollPos;
689 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
694 /***********************************************************************
695 * MENU_FindItemByCoords
697 * Find the item at the specified coordinates (screen coords). Does
698 * not work for child windows and therefore should not be called for
699 * an arbitrary system menu.
701 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
702 POINT pt, UINT *pos )
704 MENUITEM *item;
705 UINT i;
706 RECT rect;
708 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
709 pt.x -= rect.left;
710 pt.y -= rect.top;
711 item = menu->items;
712 for (i = 0; i < menu->nItems; i++, item++)
714 rect = item->rect;
715 MENU_AdjustMenuItemRect(menu, &rect);
716 if (PtInRect(&rect, pt))
718 if (pos) *pos = i;
719 return item;
722 return NULL;
726 /***********************************************************************
727 * MENU_FindItemByKey
729 * Find the menu item selected by a key press.
730 * Return item id, -1 if none, -2 if we should close the menu.
732 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
733 WCHAR key, BOOL forceMenuChar )
735 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
737 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
739 if (hmenu)
741 POPUPMENU *menu = MENU_GetMenu( hmenu );
742 MENUITEM *item = menu->items;
743 LRESULT menuchar;
745 if( !forceMenuChar )
747 UINT i;
749 for (i = 0; i < menu->nItems; i++, item++)
751 if( item->text)
753 WCHAR *p = item->text - 2;
756 p = strchrW (p + 2, '&');
758 while (p != NULL && p [1] == '&');
759 if (p && (toupperW(p[1]) == toupperW(key))) return i;
763 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
764 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
765 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
766 if (HIWORD(menuchar) == 1) return (UINT)(-2);
768 return (UINT)(-1);
772 /***********************************************************************
773 * MENU_GetBitmapItemSize
775 * Get the size of a bitmap item.
777 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
778 HWND hwndOwner)
780 BITMAP bm;
781 HBITMAP bmp = lpitem->hbmpItem;
783 size->cx = size->cy = 0;
785 /* check if there is a magic menu item associated with this item */
786 switch( (INT_PTR) bmp )
788 case (INT_PTR)HBMMENU_CALLBACK:
790 MEASUREITEMSTRUCT measItem;
791 measItem.CtlType = ODT_MENU;
792 measItem.CtlID = 0;
793 measItem.itemID = lpitem->wID;
794 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
795 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
796 measItem.itemData = lpitem->dwItemData;
797 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
798 size->cx = measItem.itemWidth;
799 size->cy = measItem.itemHeight;
800 return;
802 break;
803 case (INT_PTR)HBMMENU_SYSTEM:
804 if (lpitem->dwItemData)
806 bmp = (HBITMAP)lpitem->dwItemData;
807 break;
809 /* fall through */
810 case (INT_PTR)HBMMENU_MBAR_RESTORE:
811 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
812 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
813 case (INT_PTR)HBMMENU_MBAR_CLOSE:
814 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
815 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
816 size->cy = size->cx;
817 return;
818 case (INT_PTR)HBMMENU_POPUP_CLOSE:
819 case (INT_PTR)HBMMENU_POPUP_RESTORE:
820 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
821 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
822 FIXME("Magic %p not implemented\n", bmp );
823 return;
825 if (GetObjectW(bmp, sizeof(bm), &bm ))
827 size->cx = bm.bmWidth;
828 size->cy = bm.bmHeight;
832 /***********************************************************************
833 * MENU_DrawBitmapItem
835 * Draw a bitmap item.
837 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
838 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
840 BITMAP bm;
841 DWORD rop;
842 HDC hdcMem;
843 HBITMAP bmp;
844 int w = rect->right - rect->left;
845 int h = rect->bottom - rect->top;
846 int bmp_xoffset = 0;
847 int left, top;
848 HBITMAP hbmToDraw = lpitem->hbmpItem;
849 bmp = hbmToDraw;
851 /* Check if there is a magic menu item associated with this item */
852 if (IS_MAGIC_BITMAP(hbmToDraw))
854 UINT flags = 0;
855 RECT r;
857 switch((INT_PTR)hbmToDraw)
859 case (INT_PTR)HBMMENU_SYSTEM:
860 if (lpitem->dwItemData)
862 bmp = (HBITMAP)lpitem->dwItemData;
863 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
865 else
867 static HBITMAP hBmpSysMenu;
869 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
870 bmp = hBmpSysMenu;
871 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
872 /* only use right half of the bitmap */
873 bmp_xoffset = bm.bmWidth / 2;
874 bm.bmWidth -= bmp_xoffset;
876 goto got_bitmap;
877 case (INT_PTR)HBMMENU_MBAR_RESTORE:
878 flags = DFCS_CAPTIONRESTORE;
879 break;
880 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
881 flags = DFCS_CAPTIONMIN;
882 break;
883 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
884 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
885 break;
886 case (INT_PTR)HBMMENU_MBAR_CLOSE:
887 flags = DFCS_CAPTIONCLOSE;
888 break;
889 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
890 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
891 break;
892 case (INT_PTR)HBMMENU_CALLBACK:
894 DRAWITEMSTRUCT drawItem;
895 drawItem.CtlType = ODT_MENU;
896 drawItem.CtlID = 0;
897 drawItem.itemID = lpitem->wID;
898 drawItem.itemAction = odaction;
899 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
900 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
901 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
902 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
903 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
904 drawItem.hwndItem = (HWND)hmenu;
905 drawItem.hDC = hdc;
906 drawItem.itemData = lpitem->dwItemData;
907 drawItem.rcItem = *rect;
908 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
909 return;
911 break;
912 case (INT_PTR)HBMMENU_POPUP_CLOSE:
913 case (INT_PTR)HBMMENU_POPUP_RESTORE:
914 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
915 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
916 default:
917 FIXME("Magic %p not implemented\n", hbmToDraw);
918 return;
920 r = *rect;
921 InflateRect( &r, -1, -1 );
922 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
923 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
924 return;
927 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
929 got_bitmap:
930 hdcMem = CreateCompatibleDC( hdc );
931 SelectObject( hdcMem, bmp );
933 /* handle fontsize > bitmap_height */
934 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
935 left=rect->left;
936 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
937 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
938 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
939 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
940 DeleteDC( hdcMem );
944 /***********************************************************************
945 * MENU_CalcItemSize
947 * Calculate the size of the menu item and store it in lpitem->rect.
949 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
950 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
952 WCHAR *p;
953 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
954 UINT arrow_bitmap_width;
955 BITMAP bm;
956 INT itemheight;
958 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
959 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
960 (menuBar ? " (MenuBar)" : ""));
962 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
963 arrow_bitmap_width = bm.bmWidth;
965 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
966 if( !menucharsize.cx ) {
967 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
968 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
969 * but it is unlikely an application will depend on that */
970 ODitemheight = HIWORD( GetDialogBaseUnits());
973 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
975 if (lpitem->fType & MF_OWNERDRAW)
977 MEASUREITEMSTRUCT mis;
978 mis.CtlType = ODT_MENU;
979 mis.CtlID = 0;
980 mis.itemID = lpitem->wID;
981 mis.itemData = lpitem->dwItemData;
982 mis.itemHeight = ODitemheight;
983 mis.itemWidth = 0;
984 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
985 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
986 * width of a menufont character to the width of an owner-drawn menu.
988 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
989 if (menuBar) {
990 /* under at least win95 you seem to be given a standard
991 height for the menu and the height value is ignored */
992 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
993 } else
994 lpitem->rect.bottom += mis.itemHeight;
996 TRACE("id=%04lx size=%dx%d\n",
997 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
998 lpitem->rect.bottom-lpitem->rect.top);
999 return;
1002 if (lpitem->fType & MF_SEPARATOR)
1004 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1005 if( !menuBar)
1006 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1007 return;
1010 itemheight = 0;
1011 lpitem->xTab = 0;
1013 if (!menuBar) {
1014 if (lpitem->hbmpItem) {
1015 SIZE size;
1017 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1018 /* Keep the size of the bitmap in callback mode to be able
1019 * to draw it correctly */
1020 lpitem->bmpsize = size;
1021 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1022 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1023 lpitem->rect.right += size.cx + 2;
1024 itemheight = size.cy + 2;
1026 if( !(lppop->dwStyle & MNS_NOCHECK))
1027 lpitem->rect.right += check_bitmap_width;
1028 lpitem->rect.right += 4 + menucharsize.cx;
1029 lpitem->xTab = lpitem->rect.right;
1030 lpitem->rect.right += arrow_bitmap_width;
1031 } else if (lpitem->hbmpItem) { /* menuBar */
1032 SIZE size;
1034 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1035 lpitem->bmpsize = size;
1036 lpitem->rect.right += size.cx;
1037 if( lpitem->text) lpitem->rect.right += 2;
1038 itemheight = size.cy;
1041 /* it must be a text item - unless it's the system menu */
1042 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1043 HFONT hfontOld = NULL;
1044 RECT rc = lpitem->rect;
1045 LONG txtheight, txtwidth;
1047 if ( lpitem->fState & MFS_DEFAULT ) {
1048 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1050 if (menuBar) {
1051 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1052 DT_SINGLELINE|DT_CALCRECT);
1053 lpitem->rect.right += rc.right - rc.left;
1054 itemheight = max( max( itemheight, txtheight),
1055 GetSystemMetrics( SM_CYMENU) - 1);
1056 lpitem->rect.right += 2 * menucharsize.cx;
1057 } else {
1058 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1059 RECT tmprc = rc;
1060 LONG tmpheight;
1061 int n = (int)( p - lpitem->text);
1062 /* Item contains a tab (only meaningful in popup menus) */
1063 /* get text size before the tab */
1064 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1065 DT_SINGLELINE|DT_CALCRECT);
1066 txtwidth = rc.right - rc.left;
1067 p += 1; /* advance past the Tab */
1068 /* get text size after the tab */
1069 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1070 DT_SINGLELINE|DT_CALCRECT);
1071 lpitem->xTab += txtwidth;
1072 txtheight = max( txtheight, tmpheight);
1073 txtwidth += menucharsize.cx + /* space for the tab */
1074 tmprc.right - tmprc.left; /* space for the short cut */
1075 } else {
1076 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1077 DT_SINGLELINE|DT_CALCRECT);
1078 txtwidth = rc.right - rc.left;
1079 lpitem->xTab += txtwidth;
1081 lpitem->rect.right += 2 + txtwidth;
1082 itemheight = max( itemheight,
1083 max( txtheight + 2, menucharsize.cy + 4));
1085 if (hfontOld) SelectObject (hdc, hfontOld);
1086 } else if( menuBar) {
1087 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1089 lpitem->rect.bottom += itemheight;
1090 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1094 /***********************************************************************
1095 * MENU_GetMaxPopupHeight
1097 static UINT
1098 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1100 if (lppop->cyMax)
1101 return lppop->cyMax;
1102 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1106 /***********************************************************************
1107 * MENU_PopupMenuCalcSize
1109 * Calculate the size of a popup menu.
1111 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1113 MENUITEM *lpitem;
1114 HDC hdc;
1115 UINT start, i;
1116 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1118 lppop->Width = lppop->Height = 0;
1119 if (lppop->nItems == 0) return;
1120 hdc = GetDC( 0 );
1122 SelectObject( hdc, get_menu_font(FALSE));
1124 start = 0;
1125 maxX = 2 + 1;
1127 lppop->maxBmpSize.cx = 0;
1128 lppop->maxBmpSize.cy = 0;
1130 while (start < lppop->nItems)
1132 lpitem = &lppop->items[start];
1133 orgX = maxX;
1134 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1135 orgX += MENU_COL_SPACE;
1136 orgY = MENU_TOP_MARGIN;
1138 maxTab = maxTabWidth = 0;
1139 /* Parse items until column break or end of menu */
1140 for (i = start; i < lppop->nItems; i++, lpitem++)
1142 if ((i != start) &&
1143 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1145 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1146 maxX = max( maxX, lpitem->rect.right );
1147 orgY = lpitem->rect.bottom;
1148 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1150 maxTab = max( maxTab, lpitem->xTab );
1151 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1155 /* Finish the column (set all items to the largest width found) */
1156 maxX = max( maxX, maxTab + maxTabWidth );
1157 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1159 lpitem->rect.right = maxX;
1160 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1161 lpitem->xTab = maxTab;
1164 lppop->Height = max( lppop->Height, orgY );
1167 lppop->Width = maxX;
1169 /* space for 3d border */
1170 lppop->Height += MENU_BOTTOM_MARGIN;
1171 lppop->Width += 2;
1173 /* Adjust popup height if it exceeds maximum */
1174 maxHeight = MENU_GetMaxPopupHeight(lppop);
1175 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1176 if (lppop->Height >= maxHeight)
1178 lppop->Height = maxHeight;
1179 lppop->bScrolling = TRUE;
1181 else
1183 lppop->bScrolling = FALSE;
1186 ReleaseDC( 0, hdc );
1190 /***********************************************************************
1191 * MENU_MenuBarCalcSize
1193 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1194 * height is off by 1 pixel which causes lengthy window relocations when
1195 * active document window is maximized/restored.
1197 * Calculate the size of the menu bar.
1199 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1200 LPPOPUPMENU lppop, HWND hwndOwner )
1202 MENUITEM *lpitem;
1203 UINT start, i, helpPos;
1204 int orgX, orgY, maxY;
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 = ~0U;
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 == ~0U) && (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 if (helpPos == ~0U) return;
1252 lpitem = &lppop->items[lppop->nItems-1];
1253 orgY = lpitem->rect.top;
1254 orgX = lprect->right;
1255 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1256 if (lpitem->rect.top != orgY) break; /* Other line */
1257 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1258 lpitem->rect.left += orgX - lpitem->rect.right;
1259 lpitem->rect.right = orgX;
1260 orgX = lpitem->rect.left;
1265 /***********************************************************************
1266 * MENU_DrawScrollArrows
1268 * Draw scroll arrows.
1270 static void
1271 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1273 HDC hdcMem = CreateCompatibleDC(hdc);
1274 HBITMAP hOrigBitmap;
1275 UINT arrow_bitmap_width, arrow_bitmap_height;
1276 BITMAP bmp;
1277 RECT rect;
1279 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1280 arrow_bitmap_width = bmp.bmWidth;
1281 arrow_bitmap_height = bmp.bmHeight;
1284 if (lppop->nScrollPos)
1285 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1286 else
1287 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1288 rect.left = 0;
1289 rect.top = 0;
1290 rect.right = lppop->Width;
1291 rect.bottom = arrow_bitmap_height;
1292 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1293 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1294 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1295 rect.top = lppop->Height - arrow_bitmap_height;
1296 rect.bottom = lppop->Height;
1297 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1298 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1299 SelectObject(hdcMem, get_down_arrow_bitmap());
1300 else
1301 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1302 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1303 lppop->Height - arrow_bitmap_height,
1304 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1305 SelectObject(hdcMem, hOrigBitmap);
1306 DeleteDC(hdcMem);
1310 /***********************************************************************
1311 * draw_popup_arrow
1313 * Draws the popup-menu arrow.
1315 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1316 UINT arrow_bitmap_height)
1318 HDC hdcMem = CreateCompatibleDC( hdc );
1319 HBITMAP hOrigBitmap;
1321 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1322 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1323 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1324 arrow_bitmap_width, arrow_bitmap_height,
1325 hdcMem, 0, 0, SRCCOPY );
1326 SelectObject( hdcMem, hOrigBitmap );
1327 DeleteDC( hdcMem );
1329 /***********************************************************************
1330 * MENU_DrawMenuItem
1332 * Draw a single menu item.
1334 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1335 UINT height, BOOL menuBar, UINT odaction )
1337 RECT rect;
1338 BOOL flat_menu = FALSE;
1339 int bkgnd;
1340 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1341 POPUPMENU *menu = MENU_GetMenu(hmenu);
1342 RECT bmprc;
1344 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1346 if (!menuBar) {
1347 BITMAP bmp;
1348 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1349 arrow_bitmap_width = bmp.bmWidth;
1350 arrow_bitmap_height = bmp.bmHeight;
1353 if (lpitem->fType & MF_SYSMENU)
1355 if( !IsIconic(hwnd) )
1356 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1357 return;
1360 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1361 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1363 /* Setup colors */
1365 if (lpitem->fState & MF_HILITE)
1367 if(menuBar && !flat_menu) {
1368 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1369 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1370 } else {
1371 if(lpitem->fState & MF_GRAYED)
1372 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1373 else
1374 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1375 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1378 else
1380 if (lpitem->fState & MF_GRAYED)
1381 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1382 else
1383 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1384 SetBkColor( hdc, GetSysColor( bkgnd ) );
1387 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1388 rect = lpitem->rect;
1389 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1391 if (lpitem->fType & MF_OWNERDRAW)
1394 ** Experimentation under Windows reveals that an owner-drawn
1395 ** menu is given the rectangle which includes the space it requested
1396 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1397 ** and a popup-menu arrow. This is the value of lpitem->rect.
1398 ** Windows will leave all drawing to the application except for
1399 ** the popup-menu arrow. Windows always draws that itself, after
1400 ** the menu owner has finished drawing.
1402 DRAWITEMSTRUCT dis;
1404 dis.CtlType = ODT_MENU;
1405 dis.CtlID = 0;
1406 dis.itemID = lpitem->wID;
1407 dis.itemData = lpitem->dwItemData;
1408 dis.itemState = 0;
1409 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1410 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1411 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1412 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1413 dis.hwndItem = (HWND)hmenu;
1414 dis.hDC = hdc;
1415 dis.rcItem = rect;
1416 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1417 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1418 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1419 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1420 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1421 /* Draw the popup-menu arrow */
1422 if (lpitem->fType & MF_POPUP)
1423 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1424 arrow_bitmap_height);
1425 return;
1428 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1430 if (lpitem->fState & MF_HILITE)
1432 if (flat_menu)
1434 InflateRect (&rect, -1, -1);
1435 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1436 InflateRect (&rect, 1, 1);
1437 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1439 else
1441 if(menuBar)
1442 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1443 else
1444 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1447 else
1448 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1450 SetBkMode( hdc, TRANSPARENT );
1452 /* vertical separator */
1453 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1455 HPEN oldPen;
1456 RECT rc = rect;
1458 rc.left -= MENU_COL_SPACE / 2 + 1;
1459 rc.top = 3;
1460 rc.bottom = height - 3;
1461 if (flat_menu)
1463 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1464 MoveToEx( hdc, rc.left, rc.top, NULL );
1465 LineTo( hdc, rc.left, rc.bottom );
1466 SelectObject( hdc, oldPen );
1468 else
1469 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1472 /* horizontal separator */
1473 if (lpitem->fType & MF_SEPARATOR)
1475 HPEN oldPen;
1476 RECT rc = rect;
1478 rc.left++;
1479 rc.right--;
1480 rc.top = ( rc.top + rc.bottom) / 2;
1481 if (flat_menu)
1483 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1484 MoveToEx( hdc, rc.left, rc.top, NULL );
1485 LineTo( hdc, rc.right, rc.top );
1486 SelectObject( hdc, oldPen );
1488 else
1489 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1490 return;
1493 /* helper lines for debugging */
1494 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1495 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1496 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1497 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1500 if (lpitem->hbmpItem) {
1501 /* calculate the bitmap rectangle in coordinates relative
1502 * to the item rectangle */
1503 if( menuBar) {
1504 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1505 bmprc.left = 3;
1506 else
1507 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1509 else if (menu->dwStyle & MNS_NOCHECK)
1510 bmprc.left = 4;
1511 else if (menu->dwStyle & MNS_CHECKORBMP)
1512 bmprc.left = 2;
1513 else
1514 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1515 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1516 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1517 bmprc.top = 0;
1518 else
1519 bmprc.top = (rect.bottom - rect.top -
1520 lpitem->bmpsize.cy) / 2;
1521 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1524 if (!menuBar)
1526 HBITMAP bm;
1527 INT y = rect.top + rect.bottom;
1528 RECT rc = rect;
1529 int checked = FALSE;
1530 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1531 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1532 /* Draw the check mark
1534 * FIXME:
1535 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1537 if( !(menu->dwStyle & MNS_NOCHECK)) {
1538 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1539 lpitem->hUnCheckBit;
1540 if (bm) /* we have a custom bitmap */
1542 HDC hdcMem = CreateCompatibleDC( hdc );
1544 SelectObject( hdcMem, bm );
1545 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1546 check_bitmap_width, check_bitmap_height,
1547 hdcMem, 0, 0, SRCCOPY );
1548 DeleteDC( hdcMem );
1549 checked = TRUE;
1551 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1553 RECT r;
1554 HBITMAP bm = CreateBitmap( check_bitmap_width,
1555 check_bitmap_height, 1, 1, NULL );
1556 HDC hdcMem = CreateCompatibleDC( hdc );
1558 SelectObject( hdcMem, bm );
1559 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1560 DrawFrameControl( hdcMem, &r, DFC_MENU,
1561 (lpitem->fType & MFT_RADIOCHECK) ?
1562 DFCS_MENUBULLET : DFCS_MENUCHECK );
1563 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1564 hdcMem, 0, 0, SRCCOPY );
1565 DeleteDC( hdcMem );
1566 DeleteObject( bm );
1567 checked = TRUE;
1570 if( lpitem->hbmpItem &&
1571 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1572 POINT origorg;
1573 /* some applications make this assumption on the DC's origin */
1574 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1575 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1576 odaction, FALSE);
1577 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1579 /* Draw the popup-menu arrow */
1580 if (lpitem->fType & MF_POPUP)
1581 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1582 arrow_bitmap_height);
1583 rect.left += 4;
1584 if( !(menu->dwStyle & MNS_NOCHECK))
1585 rect.left += check_bitmap_width;
1586 rect.right -= arrow_bitmap_width;
1588 else if( lpitem->hbmpItem)
1589 { /* Draw the bitmap */
1590 POINT origorg;
1592 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1593 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1594 odaction, menuBar);
1595 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1597 /* process text if present */
1598 if (lpitem->text)
1600 register int i;
1601 HFONT hfontOld = 0;
1603 UINT uFormat = (menuBar) ?
1604 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1605 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1607 if( !(menu->dwStyle & MNS_CHECKORBMP))
1608 rect.left += menu->maxBmpSize.cx;
1610 if ( lpitem->fState & MFS_DEFAULT )
1612 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1615 if (menuBar) {
1616 if( lpitem->hbmpItem)
1617 rect.left += lpitem->bmpsize.cx;
1618 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1619 rect.left += menucharsize.cx;
1620 rect.right -= menucharsize.cx;
1623 for (i = 0; lpitem->text[i]; i++)
1624 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1625 break;
1627 if(lpitem->fState & MF_GRAYED)
1629 if (!(lpitem->fState & MF_HILITE) )
1631 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1632 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1633 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1634 --rect.left; --rect.top; --rect.right; --rect.bottom;
1636 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1639 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1641 /* paint the shortcut text */
1642 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1644 if (lpitem->text[i] == '\t')
1646 rect.left = lpitem->xTab;
1647 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1649 else
1651 rect.right = lpitem->xTab;
1652 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1655 if(lpitem->fState & MF_GRAYED)
1657 if (!(lpitem->fState & MF_HILITE) )
1659 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1660 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1661 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1662 --rect.left; --rect.top; --rect.right; --rect.bottom;
1664 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1666 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1669 if (hfontOld)
1670 SelectObject (hdc, hfontOld);
1675 /***********************************************************************
1676 * MENU_DrawPopupMenu
1678 * Paint a popup menu.
1680 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1682 HBRUSH hPrevBrush = 0;
1683 RECT rect;
1685 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1687 GetClientRect( hwnd, &rect );
1689 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1690 && (SelectObject( hdc, get_menu_font(FALSE))))
1692 HPEN hPrevPen;
1694 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1696 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1697 if( hPrevPen )
1699 POPUPMENU *menu;
1700 BOOL flat_menu = FALSE;
1702 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1703 if (flat_menu)
1704 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1705 else
1706 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1708 if( (menu = MENU_GetMenu( hmenu )))
1710 /* draw menu items */
1711 if( menu->nItems)
1713 MENUITEM *item;
1714 UINT u;
1716 item = menu->items;
1717 for( u = menu->nItems; u > 0; u--, item++)
1718 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1719 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1721 /* draw scroll arrows */
1722 if (menu->bScrolling)
1723 MENU_DrawScrollArrows(menu, hdc);
1725 } else
1727 SelectObject( hdc, hPrevBrush );
1732 /***********************************************************************
1733 * MENU_DrawMenuBar
1735 * Paint a menu bar. Returns the height of the menu bar.
1736 * called from [windows/nonclient.c]
1738 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1739 BOOL suppress_draw)
1741 LPPOPUPMENU lppop;
1742 HFONT hfontOld = 0;
1743 HMENU hMenu = GetMenu(hwnd);
1745 lppop = MENU_GetMenu( hMenu );
1746 if (lppop == NULL || lprect == NULL)
1748 return GetSystemMetrics(SM_CYMENU);
1751 if (suppress_draw)
1753 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1755 if (lppop->Height == 0)
1756 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1758 lprect->bottom = lprect->top + lppop->Height;
1760 if (hfontOld) SelectObject( hDC, hfontOld);
1761 return lppop->Height;
1763 else
1764 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1768 /***********************************************************************
1769 * MENU_ShowPopup
1771 * Display a popup menu.
1773 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1774 INT x, INT y, INT xanchor, INT yanchor )
1776 POPUPMENU *menu;
1777 INT width, height;
1778 POINT pt;
1779 HMONITOR monitor;
1780 MONITORINFO info;
1782 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1783 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1785 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1786 if (menu->FocusedItem != NO_SELECTED_ITEM)
1788 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1789 menu->FocusedItem = NO_SELECTED_ITEM;
1792 /* store the owner for DrawItem */
1793 menu->hwndOwner = hwndOwner;
1795 menu->nScrollPos = 0;
1796 MENU_PopupMenuCalcSize( menu );
1798 /* adjust popup menu pos so that it fits within the desktop */
1800 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1801 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1803 /* FIXME: should use item rect */
1804 pt.x = x;
1805 pt.y = y;
1806 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1807 info.cbSize = sizeof(info);
1808 GetMonitorInfoW( monitor, &info );
1810 if( flags & TPM_RIGHTALIGN ) x -= width;
1811 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1813 if( flags & TPM_BOTTOMALIGN ) y -= height;
1814 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1816 if( x + width > info.rcWork.right)
1818 if( xanchor && x >= width - xanchor )
1819 x -= width - xanchor;
1821 if( x + width > info.rcWork.right)
1822 x = info.rcWork.right - width;
1824 if( x < info.rcWork.left ) x = info.rcWork.left;
1826 if( y + height > info.rcWork.bottom)
1828 if( yanchor && y >= height + yanchor )
1829 y -= height + yanchor;
1831 if( y + height > info.rcWork.bottom)
1832 y = info.rcWork.bottom - height;
1834 if( y < info.rcWork.top ) y = info.rcWork.top;
1836 /* NOTE: In Windows, top menu popup is not owned. */
1837 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1838 WS_POPUP, x, y, width, height,
1839 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1840 (LPVOID)hmenu );
1841 if( !menu->hWnd ) return FALSE;
1842 if (!top_popup) top_popup = menu->hWnd;
1844 /* Display the window */
1846 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1847 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1848 UpdateWindow( menu->hWnd );
1849 return TRUE;
1853 /***********************************************************************
1854 * MENU_EnsureMenuItemVisible
1856 static void
1857 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1859 if (lppop->bScrolling)
1861 MENUITEM *item = &lppop->items[wIndex];
1862 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1863 UINT nOldPos = lppop->nScrollPos;
1864 RECT rc;
1865 UINT arrow_bitmap_height;
1866 BITMAP bmp;
1868 GetClientRect(lppop->hWnd, &rc);
1870 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1871 arrow_bitmap_height = bmp.bmHeight;
1873 rc.top += arrow_bitmap_height;
1874 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1876 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1877 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1880 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1881 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1882 MENU_DrawScrollArrows(lppop, hdc);
1884 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1886 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1887 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1888 MENU_DrawScrollArrows(lppop, hdc);
1894 /***********************************************************************
1895 * MENU_SelectItem
1897 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1898 BOOL sendMenuSelect, HMENU topmenu )
1900 LPPOPUPMENU lppop;
1901 HDC hdc;
1903 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1905 lppop = MENU_GetMenu( hmenu );
1906 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1908 if (lppop->FocusedItem == wIndex) return;
1909 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1910 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1911 if (!top_popup) top_popup = lppop->hWnd;
1913 SelectObject( hdc, get_menu_font(FALSE));
1915 /* Clear previous highlighted item */
1916 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1918 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1919 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1920 lppop->Height, !(lppop->wFlags & MF_POPUP),
1921 ODA_SELECT );
1924 /* Highlight new item (if any) */
1925 lppop->FocusedItem = wIndex;
1926 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1928 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1929 lppop->items[wIndex].fState |= MF_HILITE;
1930 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1931 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1932 &lppop->items[wIndex], lppop->Height,
1933 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1935 if (sendMenuSelect)
1937 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1938 SendMessageW( hwndOwner, WM_MENUSELECT,
1939 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1940 ip->fType | ip->fState |
1941 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1944 else if (sendMenuSelect) {
1945 if(topmenu){
1946 int pos;
1947 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1948 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1949 MENUITEM *ip = &ptm->items[pos];
1950 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1951 ip->fType | ip->fState |
1952 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1956 ReleaseDC( lppop->hWnd, hdc );
1960 /***********************************************************************
1961 * MENU_MoveSelection
1963 * Moves currently selected item according to the offset parameter.
1964 * If there is no selection then it should select the last item if
1965 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1967 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1969 INT i;
1970 POPUPMENU *menu;
1972 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1974 menu = MENU_GetMenu( hmenu );
1975 if ((!menu) || (!menu->items)) return;
1977 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1979 if( menu->nItems == 1 ) return; else
1980 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1981 ; i += offset)
1982 if (!(menu->items[i].fType & MF_SEPARATOR))
1984 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1985 return;
1989 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1990 i >= 0 && i < menu->nItems ; i += offset)
1991 if (!(menu->items[i].fType & MF_SEPARATOR))
1993 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1994 return;
1999 /**********************************************************************
2000 * MENU_SetItemData
2002 * Set an item's flags, id and text ptr. Called by InsertMenu() and
2003 * ModifyMenu().
2005 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
2006 LPCWSTR str )
2008 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2009 TRACE("flags=%x str=%p\n", flags, str);
2011 if (IS_STRING_ITEM(flags))
2013 LPWSTR prevText = item->text;
2014 if (!str)
2016 flags |= MF_SEPARATOR;
2017 item->text = NULL;
2019 else
2021 LPWSTR text;
2022 /* Item beginning with a backspace is a help item */
2023 if (*str == '\b')
2025 flags |= MF_HELP;
2026 str++;
2028 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2029 return FALSE;
2030 strcpyW( text, str );
2031 item->text = text;
2033 item->hbmpItem = NULL;
2034 HeapFree( GetProcessHeap(), 0, prevText );
2036 else if(( flags & MFT_BITMAP)) {
2037 item->hbmpItem = HBITMAP_32(LOWORD(str));
2038 /* setting bitmap clears text */
2039 HeapFree( GetProcessHeap(), 0, item->text );
2040 item->text = NULL;
2043 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2045 if (flags & MF_OWNERDRAW)
2046 item->dwItemData = (DWORD_PTR)str;
2047 else
2048 item->dwItemData = 0;
2050 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2051 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2053 if (flags & MF_POPUP)
2055 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2056 if (menu) menu->wFlags |= MF_POPUP;
2057 else
2059 item->wID = 0;
2060 item->hSubMenu = 0;
2061 item->fType = 0;
2062 item->fState = 0;
2063 return FALSE;
2067 item->wID = id;
2068 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2070 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2071 flags |= MF_POPUP; /* keep popup */
2073 item->fType = flags & TYPE_MASK;
2074 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2075 for ModifyMenu, but Windows accepts it */
2076 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2078 /* Don't call SetRectEmpty here! */
2080 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2081 return TRUE;
2085 /**********************************************************************
2086 * MENU_InsertItem
2088 * Insert (allocate) a new item into a menu.
2090 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2092 MENUITEM *newItems;
2093 POPUPMENU *menu;
2095 if (!(menu = MENU_GetMenu(hMenu)))
2096 return NULL;
2098 /* Find where to insert new item */
2100 if (flags & MF_BYPOSITION) {
2101 if (pos > menu->nItems)
2102 pos = menu->nItems;
2103 } else {
2104 if (!MENU_FindItem( &hMenu, &pos, flags ))
2105 pos = menu->nItems;
2106 else {
2107 if (!(menu = MENU_GetMenu( hMenu )))
2108 return NULL;
2112 /* Make sure that MDI system buttons stay on the right side.
2113 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2114 * regardless of their id.
2116 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2117 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2118 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2119 pos--;
2121 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2123 /* Create new items array */
2125 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2126 if (!newItems)
2128 WARN("allocation failed\n" );
2129 return NULL;
2131 if (menu->nItems > 0)
2133 /* Copy the old array into the new one */
2134 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2135 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2136 (menu->nItems-pos)*sizeof(MENUITEM) );
2137 HeapFree( GetProcessHeap(), 0, menu->items );
2139 menu->items = newItems;
2140 menu->nItems++;
2141 memset( &newItems[pos], 0, sizeof(*newItems) );
2142 menu->Height = 0; /* force size recalculate */
2143 return &newItems[pos];
2147 /**********************************************************************
2148 * MENU_ParseResource
2150 * Parse a standard menu resource and add items to the menu.
2151 * Return a pointer to the end of the resource.
2153 * NOTE: flags is equivalent to the mtOption field
2155 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2157 WORD flags, id = 0;
2158 LPCSTR str;
2159 BOOL end_flag;
2163 flags = GET_WORD(res);
2164 end_flag = flags & MF_END;
2165 /* Remove MF_END because it has the same value as MF_HILITE */
2166 flags &= ~MF_END;
2167 res += sizeof(WORD);
2168 if (!(flags & MF_POPUP))
2170 id = GET_WORD(res);
2171 res += sizeof(WORD);
2173 str = res;
2174 if (!unicode) res += strlen(str) + 1;
2175 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2176 if (flags & MF_POPUP)
2178 HMENU hSubMenu = CreatePopupMenu();
2179 if (!hSubMenu) return NULL;
2180 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2181 return NULL;
2182 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2183 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2185 else /* Not a popup */
2187 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2188 else AppendMenuW( hMenu, flags, id,
2189 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2191 } while (!end_flag);
2192 return res;
2196 /**********************************************************************
2197 * MENUEX_ParseResource
2199 * Parse an extended menu resource and add items to the menu.
2200 * Return a pointer to the end of the resource.
2202 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2204 WORD resinfo;
2205 do {
2206 MENUITEMINFOW mii;
2208 mii.cbSize = sizeof(mii);
2209 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2210 mii.fType = GET_DWORD(res);
2211 res += sizeof(DWORD);
2212 mii.fState = GET_DWORD(res);
2213 res += sizeof(DWORD);
2214 mii.wID = GET_DWORD(res);
2215 res += sizeof(DWORD);
2216 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2217 res += sizeof(WORD);
2218 /* Align the text on a word boundary. */
2219 res += (~((UINT_PTR)res - 1)) & 1;
2220 mii.dwTypeData = (LPWSTR) res;
2221 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2222 /* Align the following fields on a dword boundary. */
2223 res += (~((UINT_PTR)res - 1)) & 3;
2225 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2226 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2228 if (resinfo & 1) { /* Pop-up? */
2229 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2230 res += sizeof(DWORD);
2231 mii.hSubMenu = CreatePopupMenu();
2232 if (!mii.hSubMenu)
2233 return NULL;
2234 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2235 DestroyMenu(mii.hSubMenu);
2236 return NULL;
2238 mii.fMask |= MIIM_SUBMENU;
2239 mii.fType |= MF_POPUP;
2241 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2243 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2244 mii.wID, mii.fType);
2245 mii.fType |= MF_SEPARATOR;
2247 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2248 } while (!(resinfo & MF_END));
2249 return res;
2253 /***********************************************************************
2254 * MENU_GetSubPopup
2256 * Return the handle of the selected sub-popup menu (if any).
2258 static HMENU MENU_GetSubPopup( HMENU hmenu )
2260 POPUPMENU *menu;
2261 MENUITEM *item;
2263 menu = MENU_GetMenu( hmenu );
2265 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2267 item = &menu->items[menu->FocusedItem];
2268 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2269 return item->hSubMenu;
2270 return 0;
2274 /***********************************************************************
2275 * MENU_HideSubPopups
2277 * Hide the sub-popup menus of this menu.
2279 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2280 BOOL sendMenuSelect, UINT wFlags )
2282 POPUPMENU *menu = MENU_GetMenu( hmenu );
2284 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2286 if (menu && top_popup)
2288 HMENU hsubmenu;
2289 POPUPMENU *submenu;
2290 MENUITEM *item;
2292 if (menu->FocusedItem != NO_SELECTED_ITEM)
2294 item = &menu->items[menu->FocusedItem];
2295 if (!(item->fType & MF_POPUP) ||
2296 !(item->fState & MF_MOUSESELECT)) return;
2297 item->fState &= ~MF_MOUSESELECT;
2298 hsubmenu = item->hSubMenu;
2299 } else return;
2301 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2302 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2303 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2304 DestroyWindow( submenu->hWnd );
2305 submenu->hWnd = 0;
2307 if (!(wFlags & TPM_NONOTIFY))
2308 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2309 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2314 /***********************************************************************
2315 * MENU_ShowSubPopup
2317 * Display the sub-menu of the selected item of this menu.
2318 * Return the handle of the submenu, or hmenu if no submenu to display.
2320 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2321 BOOL selectFirst, UINT wFlags )
2323 RECT rect;
2324 POPUPMENU *menu;
2325 MENUITEM *item;
2326 HDC hdc;
2328 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2330 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2332 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2334 item = &menu->items[menu->FocusedItem];
2335 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2336 return hmenu;
2338 /* message must be sent before using item,
2339 because nearly everything may be changed by the application ! */
2341 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2342 if (!(wFlags & TPM_NONOTIFY))
2343 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2344 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2346 item = &menu->items[menu->FocusedItem];
2347 rect = item->rect;
2349 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2350 if (!(item->fState & MF_HILITE))
2352 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2353 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2355 SelectObject( hdc, get_menu_font(FALSE));
2357 item->fState |= MF_HILITE;
2358 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2359 ReleaseDC( menu->hWnd, hdc );
2361 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2362 item->rect = rect;
2364 item->fState |= MF_MOUSESELECT;
2366 if (IS_SYSTEM_MENU(menu))
2368 MENU_InitSysMenuPopup(item->hSubMenu,
2369 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2370 GetClassLongW( menu->hWnd, GCL_STYLE));
2372 NC_GetSysPopupPos( menu->hWnd, &rect );
2373 rect.top = rect.bottom;
2374 rect.right = GetSystemMetrics(SM_CXSIZE);
2375 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2377 else
2379 GetWindowRect( menu->hWnd, &rect );
2380 if (menu->wFlags & MF_POPUP)
2382 RECT rc = item->rect;
2384 MENU_AdjustMenuItemRect(menu, &rc);
2386 /* The first item in the popup menu has to be at the
2387 same y position as the focused menu item */
2388 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2389 rect.top += rc.top - MENU_TOP_MARGIN;
2390 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2391 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2392 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2394 else
2396 rect.left += item->rect.left;
2397 rect.top += item->rect.bottom;
2398 rect.right = item->rect.right - item->rect.left;
2399 rect.bottom = item->rect.bottom - item->rect.top;
2403 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2404 rect.left, rect.top, rect.right, rect.bottom );
2405 if (selectFirst)
2406 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2407 return item->hSubMenu;
2412 /**********************************************************************
2413 * MENU_IsMenuActive
2415 HWND MENU_IsMenuActive(void)
2417 return top_popup;
2420 /***********************************************************************
2421 * MENU_PtMenu
2423 * Walks menu chain trying to find a menu pt maps to.
2425 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2427 POPUPMENU *menu = MENU_GetMenu( hMenu );
2428 UINT item = menu->FocusedItem;
2429 HMENU ret;
2431 /* try subpopup first (if any) */
2432 ret = (item != NO_SELECTED_ITEM &&
2433 (menu->items[item].fType & MF_POPUP) &&
2434 (menu->items[item].fState & MF_MOUSESELECT))
2435 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2437 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2439 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2440 if( menu->wFlags & MF_POPUP )
2442 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2444 else if (ht == HTSYSMENU)
2445 ret = get_win_sys_menu( menu->hWnd );
2446 else if (ht == HTMENU)
2447 ret = GetMenu( menu->hWnd );
2449 return ret;
2452 /***********************************************************************
2453 * MENU_ExecFocusedItem
2455 * Execute a menu item (for instance when user pressed Enter).
2456 * Return the wID of the executed item. Otherwise, -1 indicating
2457 * that no menu item was executed, -2 if a popup is shown;
2458 * Have to receive the flags for the TrackPopupMenu options to avoid
2459 * sending unwanted message.
2462 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2464 MENUITEM *item;
2465 POPUPMENU *menu = MENU_GetMenu( hMenu );
2467 TRACE("%p hmenu=%p\n", pmt, hMenu);
2469 if (!menu || !menu->nItems ||
2470 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2472 item = &menu->items[menu->FocusedItem];
2474 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2476 if (!(item->fType & MF_POPUP))
2478 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2480 /* If TPM_RETURNCMD is set you return the id, but
2481 do not send a message to the owner */
2482 if(!(wFlags & TPM_RETURNCMD))
2484 if( menu->wFlags & MF_SYSMENU )
2485 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2486 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2487 else
2489 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2490 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2492 if (dwStyle & MNS_NOTIFYBYPOS)
2493 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2494 (LPARAM)hMenu);
2495 else
2496 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2499 return item->wID;
2502 else
2504 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2505 return -2;
2508 return -1;
2511 /***********************************************************************
2512 * MENU_SwitchTracking
2514 * Helper function for menu navigation routines.
2516 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2518 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2519 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2521 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2523 if( pmt->hTopMenu != hPtMenu &&
2524 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2526 /* both are top level menus (system and menu-bar) */
2527 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2528 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2529 pmt->hTopMenu = hPtMenu;
2531 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2532 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2536 /***********************************************************************
2537 * MENU_ButtonDown
2539 * Return TRUE if we can go on with menu tracking.
2541 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2543 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2545 if (hPtMenu)
2547 UINT id = 0;
2548 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2549 MENUITEM *item;
2551 if( IS_SYSTEM_MENU(ptmenu) )
2552 item = ptmenu->items;
2553 else
2554 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2556 if( item )
2558 if( ptmenu->FocusedItem != id )
2559 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2561 /* If the popup menu is not already "popped" */
2562 if(!(item->fState & MF_MOUSESELECT ))
2564 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2567 return TRUE;
2569 /* Else the click was on the menu bar, finish the tracking */
2571 return FALSE;
2574 /***********************************************************************
2575 * MENU_ButtonUp
2577 * Return the value of MENU_ExecFocusedItem if
2578 * the selected item was not a popup. Else open the popup.
2579 * A -1 return value indicates that we go on with menu tracking.
2582 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2584 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2586 if (hPtMenu)
2588 UINT id = 0;
2589 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2590 MENUITEM *item;
2592 if( IS_SYSTEM_MENU(ptmenu) )
2593 item = ptmenu->items;
2594 else
2595 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2597 if( item && (ptmenu->FocusedItem == id ))
2599 debug_print_menuitem ("FocusedItem: ", item, "");
2601 if( !(item->fType & MF_POPUP) )
2603 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2604 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2605 return executedMenuId;
2608 /* If we are dealing with the top-level menu */
2609 /* and this is a click on an already "popped" item: */
2610 /* Stop the menu tracking and close the opened submenus */
2611 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2612 return 0;
2614 ptmenu->bTimeToHide = TRUE;
2616 return -1;
2620 /***********************************************************************
2621 * MENU_MouseMove
2623 * Return TRUE if we can go on with menu tracking.
2625 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2627 UINT id = NO_SELECTED_ITEM;
2628 POPUPMENU *ptmenu = NULL;
2630 if( hPtMenu )
2632 ptmenu = MENU_GetMenu( hPtMenu );
2633 if( IS_SYSTEM_MENU(ptmenu) )
2634 id = 0;
2635 else
2636 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2639 if( id == NO_SELECTED_ITEM )
2641 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2642 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2645 else if( ptmenu->FocusedItem != id )
2647 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2648 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2650 return TRUE;
2654 /***********************************************************************
2655 * MENU_DoNextMenu
2657 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2659 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2661 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2662 BOOL atEnd = FALSE;
2664 /* When skipping left, we need to do something special after the
2665 first menu. */
2666 if (vk == VK_LEFT && menu->FocusedItem == 0)
2668 atEnd = TRUE;
2670 /* When skipping right, for the non-system menu, we need to
2671 handle the last non-special menu item (ie skip any window
2672 icons such as MDI maximize, restore or close) */
2673 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2675 UINT i = menu->FocusedItem + 1;
2676 while (i < menu->nItems) {
2677 if ((menu->items[i].wID >= SC_SIZE &&
2678 menu->items[i].wID <= SC_RESTORE)) {
2679 i++;
2680 } else break;
2682 if (i == menu->nItems) {
2683 atEnd = TRUE;
2686 /* When skipping right, we need to cater for the system menu */
2687 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2689 if (menu->FocusedItem == (menu->nItems - 1)) {
2690 atEnd = TRUE;
2694 if( atEnd )
2696 MDINEXTMENU next_menu;
2697 HMENU hNewMenu;
2698 HWND hNewWnd;
2699 UINT id = 0;
2701 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2702 next_menu.hmenuNext = 0;
2703 next_menu.hwndNext = 0;
2704 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2706 TRACE("%p [%p] -> %p [%p]\n",
2707 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2709 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2711 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2712 hNewWnd = pmt->hOwnerWnd;
2713 if( IS_SYSTEM_MENU(menu) )
2715 /* switch to the menu bar */
2717 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2719 if( vk == VK_LEFT )
2721 menu = MENU_GetMenu( hNewMenu );
2722 id = menu->nItems - 1;
2724 /* Skip backwards over any system predefined icons,
2725 eg. MDI close, restore etc icons */
2726 while ((id > 0) &&
2727 (menu->items[id].wID >= SC_SIZE &&
2728 menu->items[id].wID <= SC_RESTORE)) id--;
2731 else if (style & WS_SYSMENU )
2733 /* switch to the system menu */
2734 hNewMenu = get_win_sys_menu( hNewWnd );
2736 else return FALSE;
2738 else /* application returned a new menu to switch to */
2740 hNewMenu = next_menu.hmenuNext;
2741 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2743 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2745 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2747 if (style & WS_SYSMENU &&
2748 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2750 /* get the real system menu */
2751 hNewMenu = get_win_sys_menu(hNewWnd);
2753 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2755 /* FIXME: Not sure what to do here;
2756 * perhaps try to track hNewMenu as a popup? */
2758 TRACE(" -- got confused.\n");
2759 return FALSE;
2762 else return FALSE;
2765 if( hNewMenu != pmt->hTopMenu )
2767 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2768 FALSE, 0 );
2769 if( pmt->hCurrentMenu != pmt->hTopMenu )
2770 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2773 if( hNewWnd != pmt->hOwnerWnd )
2775 pmt->hOwnerWnd = hNewWnd;
2776 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2779 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2780 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2782 return TRUE;
2784 return FALSE;
2787 /***********************************************************************
2788 * MENU_SuspendPopup
2790 * The idea is not to show the popup if the next input message is
2791 * going to hide it anyway.
2793 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2795 MSG msg;
2797 msg.hwnd = pmt->hOwnerWnd;
2799 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2800 pmt->trackFlags |= TF_SKIPREMOVE;
2802 switch( uMsg )
2804 case WM_KEYDOWN:
2805 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2806 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2808 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2809 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2810 if( msg.message == WM_KEYDOWN &&
2811 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2813 pmt->trackFlags |= TF_SUSPENDPOPUP;
2814 return TRUE;
2817 break;
2820 /* failures go through this */
2821 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2822 return FALSE;
2825 /***********************************************************************
2826 * MENU_KeyEscape
2828 * Handle a VK_ESCAPE key event in a menu.
2830 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2832 BOOL bEndMenu = TRUE;
2834 if (pmt->hCurrentMenu != pmt->hTopMenu)
2836 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2838 if (menu->wFlags & MF_POPUP)
2840 HMENU hmenutmp, hmenuprev;
2842 hmenuprev = hmenutmp = pmt->hTopMenu;
2844 /* close topmost popup */
2845 while (hmenutmp != pmt->hCurrentMenu)
2847 hmenuprev = hmenutmp;
2848 hmenutmp = MENU_GetSubPopup( hmenuprev );
2851 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2852 pmt->hCurrentMenu = hmenuprev;
2853 bEndMenu = FALSE;
2857 return bEndMenu;
2860 /***********************************************************************
2861 * MENU_KeyLeft
2863 * Handle a VK_LEFT key event in a menu.
2865 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2867 POPUPMENU *menu;
2868 HMENU hmenutmp, hmenuprev;
2869 UINT prevcol;
2871 hmenuprev = hmenutmp = pmt->hTopMenu;
2872 menu = MENU_GetMenu( hmenutmp );
2874 /* Try to move 1 column left (if possible) */
2875 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2876 NO_SELECTED_ITEM ) {
2878 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2879 prevcol, TRUE, 0 );
2880 return;
2883 /* close topmost popup */
2884 while (hmenutmp != pmt->hCurrentMenu)
2886 hmenuprev = hmenutmp;
2887 hmenutmp = MENU_GetSubPopup( hmenuprev );
2890 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2891 pmt->hCurrentMenu = hmenuprev;
2893 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2895 /* move menu bar selection if no more popups are left */
2897 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2898 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2900 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2902 /* A sublevel menu was displayed - display the next one
2903 * unless there is another displacement coming up */
2905 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2906 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2907 pmt->hTopMenu, TRUE, wFlags);
2913 /***********************************************************************
2914 * MENU_KeyRight
2916 * Handle a VK_RIGHT key event in a menu.
2918 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2920 HMENU hmenutmp;
2921 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2922 UINT nextcol;
2924 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2925 pmt->hCurrentMenu,
2926 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2927 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2929 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2931 /* If already displaying a popup, try to display sub-popup */
2933 hmenutmp = pmt->hCurrentMenu;
2934 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2936 /* if subpopup was displayed then we are done */
2937 if (hmenutmp != pmt->hCurrentMenu) return;
2940 /* Check to see if there's another column */
2941 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2942 NO_SELECTED_ITEM ) {
2943 TRACE("Going to %d.\n", nextcol );
2944 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2945 nextcol, TRUE, 0 );
2946 return;
2949 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2951 if( pmt->hCurrentMenu != pmt->hTopMenu )
2953 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2954 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2955 } else hmenutmp = 0;
2957 /* try to move to the next item */
2958 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2959 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2961 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2962 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2963 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2964 pmt->hTopMenu, TRUE, wFlags);
2968 static void CALLBACK release_capture( BOOL __normal )
2970 set_capture_window( 0, GUI_INMENUMODE, NULL );
2973 /***********************************************************************
2974 * MENU_TrackMenu
2976 * Menu tracking code.
2978 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2979 HWND hwnd, const RECT *lprect )
2981 MSG msg;
2982 POPUPMENU *menu;
2983 BOOL fRemove;
2984 INT executedMenuId = -1;
2985 MTRACKER mt;
2986 BOOL enterIdleSent = FALSE;
2987 HWND capture_win;
2989 mt.trackFlags = 0;
2990 mt.hCurrentMenu = hmenu;
2991 mt.hTopMenu = hmenu;
2992 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2993 mt.pt.x = x;
2994 mt.pt.y = y;
2996 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2997 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2999 fEndMenu = FALSE;
3000 if (!(menu = MENU_GetMenu( hmenu )))
3002 WARN("Invalid menu handle %p\n", hmenu);
3003 SetLastError(ERROR_INVALID_MENU_HANDLE);
3004 return FALSE;
3007 if (wFlags & TPM_BUTTONDOWN)
3009 /* Get the result in order to start the tracking or not */
3010 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3011 fEndMenu = !fRemove;
3014 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3016 /* owner may not be visible when tracking a popup, so use the menu itself */
3017 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3018 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3020 __TRY while (!fEndMenu)
3022 menu = MENU_GetMenu( mt.hCurrentMenu );
3023 if (!menu) /* sometimes happens if I do a window manager close */
3024 break;
3026 /* we have to keep the message in the queue until it's
3027 * clear that menu loop is not over yet. */
3029 for (;;)
3031 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3033 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3034 /* remove the message from the queue */
3035 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3037 else
3039 if (!enterIdleSent)
3041 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3042 enterIdleSent = TRUE;
3043 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3045 WaitMessage();
3049 /* check if EndMenu() tried to cancel us, by posting this message */
3050 if(msg.message == WM_CANCELMODE)
3052 /* we are now out of the loop */
3053 fEndMenu = TRUE;
3055 /* remove the message from the queue */
3056 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3058 /* break out of internal loop, ala ESCAPE */
3059 break;
3062 TranslateMessage( &msg );
3063 mt.pt = msg.pt;
3065 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3066 enterIdleSent=FALSE;
3068 fRemove = FALSE;
3069 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3072 * Use the mouse coordinates in lParam instead of those in the MSG
3073 * struct to properly handle synthetic messages. They are already
3074 * in screen coordinates.
3076 mt.pt.x = (short)LOWORD(msg.lParam);
3077 mt.pt.y = (short)HIWORD(msg.lParam);
3079 /* Find a menu for this mouse event */
3080 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3082 switch(msg.message)
3084 /* no WM_NC... messages in captured state */
3086 case WM_RBUTTONDBLCLK:
3087 case WM_RBUTTONDOWN:
3088 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3089 /* fall through */
3090 case WM_LBUTTONDBLCLK:
3091 case WM_LBUTTONDOWN:
3092 /* If the message belongs to the menu, removes it from the queue */
3093 /* Else, end menu tracking */
3094 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3095 fEndMenu = !fRemove;
3096 break;
3098 case WM_RBUTTONUP:
3099 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3100 /* fall through */
3101 case WM_LBUTTONUP:
3102 /* Check if a menu was selected by the mouse */
3103 if (hmenu)
3105 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3106 TRACE("executedMenuId %d\n", executedMenuId);
3108 /* End the loop if executedMenuId is an item ID */
3109 /* or if the job was done (executedMenuId = 0). */
3110 fEndMenu = fRemove = (executedMenuId != -1);
3112 /* No menu was selected by the mouse */
3113 /* if the function was called by TrackPopupMenu, continue
3114 with the menu tracking. If not, stop it */
3115 else
3116 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3118 break;
3120 case WM_MOUSEMOVE:
3121 /* the selected menu item must be changed every time */
3122 /* the mouse moves. */
3124 if (hmenu)
3125 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3127 } /* switch(msg.message) - mouse */
3129 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3131 fRemove = TRUE; /* Keyboard messages are always removed */
3132 switch(msg.message)
3134 case WM_KEYDOWN:
3135 case WM_SYSKEYDOWN:
3136 switch(msg.wParam)
3138 case VK_MENU:
3139 fEndMenu = TRUE;
3140 break;
3142 case VK_HOME:
3143 case VK_END:
3144 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3145 NO_SELECTED_ITEM, FALSE, 0 );
3146 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3147 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3148 break;
3150 case VK_UP:
3151 case VK_DOWN: /* If on menu bar, pull-down the menu */
3153 menu = MENU_GetMenu( mt.hCurrentMenu );
3154 if (!(menu->wFlags & MF_POPUP))
3155 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3156 else /* otherwise try to move selection */
3157 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3158 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3159 break;
3161 case VK_LEFT:
3162 MENU_KeyLeft( &mt, wFlags );
3163 break;
3165 case VK_RIGHT:
3166 MENU_KeyRight( &mt, wFlags );
3167 break;
3169 case VK_ESCAPE:
3170 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3171 break;
3173 case VK_F1:
3175 HELPINFO hi;
3176 hi.cbSize = sizeof(HELPINFO);
3177 hi.iContextType = HELPINFO_MENUITEM;
3178 if (menu->FocusedItem == NO_SELECTED_ITEM)
3179 hi.iCtrlId = 0;
3180 else
3181 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3182 hi.hItemHandle = hmenu;
3183 hi.dwContextId = menu->dwContextHelpID;
3184 hi.MousePos = msg.pt;
3185 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3186 break;
3189 default:
3190 break;
3192 break; /* WM_KEYDOWN */
3194 case WM_CHAR:
3195 case WM_SYSCHAR:
3197 UINT pos;
3199 if (msg.wParam == '\r' || msg.wParam == ' ')
3201 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3202 fEndMenu = (executedMenuId != -2);
3204 break;
3207 /* Hack to avoid control chars. */
3208 /* We will find a better way real soon... */
3209 if (msg.wParam < 32) break;
3211 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3212 LOWORD(msg.wParam), FALSE );
3213 if (pos == (UINT)-2) fEndMenu = TRUE;
3214 else if (pos == (UINT)-1) MessageBeep(0);
3215 else
3217 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3218 TRUE, 0 );
3219 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3220 fEndMenu = (executedMenuId != -2);
3223 break;
3224 } /* switch(msg.message) - kbd */
3226 else
3228 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3229 DispatchMessageW( &msg );
3230 continue;
3233 if (!fEndMenu) fRemove = TRUE;
3235 /* finally remove message from the queue */
3237 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3238 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3239 else mt.trackFlags &= ~TF_SKIPREMOVE;
3241 __FINALLY( release_capture )
3243 /* If dropdown is still painted and the close box is clicked on
3244 then the menu will be destroyed as part of the DispatchMessage above.
3245 This will then invalidate the menu handle in mt.hTopMenu. We should
3246 check for this first. */
3247 if( IsMenu( mt.hTopMenu ) )
3249 menu = MENU_GetMenu( mt.hTopMenu );
3251 if( IsWindow( mt.hOwnerWnd ) )
3253 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3255 if (menu && (menu->wFlags & MF_POPUP))
3257 DestroyWindow( menu->hWnd );
3258 menu->hWnd = 0;
3260 if (!(wFlags & TPM_NONOTIFY))
3261 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3262 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3264 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3265 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3268 /* Reset the variable for hiding menu */
3269 if( menu ) menu->bTimeToHide = FALSE;
3272 /* The return value is only used by TrackPopupMenu */
3273 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3274 if (executedMenuId == -1) executedMenuId = 0;
3275 return executedMenuId;
3278 /***********************************************************************
3279 * MENU_InitTracking
3281 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3283 POPUPMENU *menu;
3285 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3287 HideCaret(0);
3289 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3290 if (!(wFlags & TPM_NONOTIFY))
3291 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3293 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3295 if (!(wFlags & TPM_NONOTIFY))
3297 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3298 /* If an app changed/recreated menu bar entries in WM_INITMENU
3299 * menu sizes will be recalculated once the menu created/shown.
3303 /* This makes the menus of applications built with Delphi work.
3304 * It also enables menus to be displayed in more than one window,
3305 * but there are some bugs left that need to be fixed in this case.
3307 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3309 return TRUE;
3311 /***********************************************************************
3312 * MENU_ExitTracking
3314 static BOOL MENU_ExitTracking(HWND hWnd)
3316 TRACE("hwnd=%p\n", hWnd);
3318 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3319 ShowCaret(0);
3320 top_popup = 0;
3321 return TRUE;
3324 /***********************************************************************
3325 * MENU_TrackMouseMenuBar
3327 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3329 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3331 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3332 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3334 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3336 if (IsMenu(hMenu))
3338 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3339 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3340 MENU_ExitTracking(hWnd);
3345 /***********************************************************************
3346 * MENU_TrackKbdMenuBar
3348 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3350 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3352 UINT uItem = NO_SELECTED_ITEM;
3353 HMENU hTrackMenu;
3354 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3356 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3358 /* find window that has a menu */
3360 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3361 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3363 /* check if we have to track a system menu */
3365 hTrackMenu = GetMenu( hwnd );
3366 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3368 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3369 hTrackMenu = get_win_sys_menu( hwnd );
3370 uItem = 0;
3371 wParam |= HTSYSMENU; /* prevent item lookup */
3374 if (!IsMenu( hTrackMenu )) return;
3376 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3378 if( wChar && wChar != ' ' )
3380 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3381 if ( uItem >= (UINT)(-2) )
3383 if( uItem == (UINT)(-1) ) MessageBeep(0);
3384 /* schedule end of menu tracking */
3385 wFlags |= TF_ENDMENU;
3386 goto track_menu;
3390 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3392 if (!(wParam & HTSYSMENU) || wChar == ' ')
3394 if( uItem == NO_SELECTED_ITEM )
3395 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3396 else
3397 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3400 track_menu:
3401 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3402 MENU_ExitTracking( hwnd );
3406 /**********************************************************************
3407 * TrackPopupMenu (USER32.@)
3409 * Like the win32 API, the function return the command ID only if the
3410 * flag TPM_RETURNCMD is on.
3413 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3414 INT nReserved, HWND hWnd, const RECT *lpRect )
3416 BOOL ret = FALSE;
3418 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3419 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3421 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3423 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3424 if (!(wFlags & TPM_NONOTIFY))
3425 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3427 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3428 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3429 MENU_ExitTracking(hWnd);
3431 return ret;
3434 /**********************************************************************
3435 * TrackPopupMenuEx (USER32.@)
3437 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3438 HWND hWnd, LPTPMPARAMS lpTpm )
3440 FIXME("not fully implemented\n" );
3441 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3442 lpTpm ? &lpTpm->rcExclude : NULL );
3445 /***********************************************************************
3446 * PopupMenuWndProc
3448 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3450 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3452 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3454 switch(message)
3456 case WM_CREATE:
3458 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3459 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3460 return 0;
3463 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3464 return MA_NOACTIVATE;
3466 case WM_PAINT:
3468 PAINTSTRUCT ps;
3469 BeginPaint( hwnd, &ps );
3470 MENU_DrawPopupMenu( hwnd, ps.hdc,
3471 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3472 EndPaint( hwnd, &ps );
3473 return 0;
3475 case WM_ERASEBKGND:
3476 return 1;
3478 case WM_DESTROY:
3479 /* zero out global pointer in case resident popup window was destroyed. */
3480 if (hwnd == top_popup) top_popup = 0;
3481 break;
3483 case WM_SHOWWINDOW:
3485 if( wParam )
3487 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3489 else
3490 SetWindowLongPtrW( hwnd, 0, 0 );
3491 break;
3493 case MM_SETMENUHANDLE:
3494 SetWindowLongPtrW( hwnd, 0, wParam );
3495 break;
3497 case MM_GETMENUHANDLE:
3498 return GetWindowLongPtrW( hwnd, 0 );
3500 default:
3501 return DefWindowProcW( hwnd, message, wParam, lParam );
3503 return 0;
3507 /***********************************************************************
3508 * MENU_GetMenuBarHeight
3510 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3512 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3513 INT orgX, INT orgY )
3515 HDC hdc;
3516 RECT rectBar;
3517 LPPOPUPMENU lppop;
3519 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3521 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3523 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3524 SelectObject( hdc, get_menu_font(FALSE));
3525 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3526 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3527 ReleaseDC( hwnd, hdc );
3528 return lppop->Height;
3532 /*******************************************************************
3533 * ChangeMenuA (USER32.@)
3535 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3536 UINT id, UINT flags )
3538 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3539 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3540 id, data );
3541 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3542 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3543 id, data );
3544 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3545 flags & MF_BYPOSITION ? pos : id,
3546 flags & ~MF_REMOVE );
3547 /* Default: MF_INSERT */
3548 return InsertMenuA( hMenu, pos, flags, id, data );
3552 /*******************************************************************
3553 * ChangeMenuW (USER32.@)
3555 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3556 UINT id, UINT flags )
3558 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3559 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3560 id, data );
3561 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3562 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3563 id, data );
3564 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3565 flags & MF_BYPOSITION ? pos : id,
3566 flags & ~MF_REMOVE );
3567 /* Default: MF_INSERT */
3568 return InsertMenuW( hMenu, pos, flags, id, data );
3572 /*******************************************************************
3573 * CheckMenuItem (USER32.@)
3575 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3577 MENUITEM *item;
3578 DWORD ret;
3580 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3581 ret = item->fState & MF_CHECKED;
3582 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3583 else item->fState &= ~MF_CHECKED;
3584 return ret;
3588 /**********************************************************************
3589 * EnableMenuItem (USER32.@)
3591 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3593 UINT oldflags;
3594 MENUITEM *item;
3595 POPUPMENU *menu;
3597 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3599 /* Get the Popupmenu to access the owner menu */
3600 if (!(menu = MENU_GetMenu(hMenu)))
3601 return (UINT)-1;
3603 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3604 return (UINT)-1;
3606 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3607 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3609 /* If the close item in the system menu change update the close button */
3610 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3612 if (menu->hSysMenuOwner != 0)
3614 RECT rc;
3615 POPUPMENU* parentMenu;
3617 /* Get the parent menu to access*/
3618 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3619 return (UINT)-1;
3621 /* Refresh the frame to reflect the change */
3622 GetWindowRect(parentMenu->hWnd, &rc);
3623 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3624 rc.bottom = 0;
3625 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3629 return oldflags;
3633 /*******************************************************************
3634 * GetMenuStringA (USER32.@)
3636 INT WINAPI GetMenuStringA(
3637 HMENU hMenu, /* [in] menuhandle */
3638 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3639 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3640 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3641 UINT wFlags /* [in] MF_ flags */
3643 MENUITEM *item;
3645 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3646 if (str && nMaxSiz) str[0] = '\0';
3647 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3648 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3649 return 0;
3651 if (!item->text) return 0;
3652 if (!str || !nMaxSiz) return strlenW(item->text);
3653 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3654 str[nMaxSiz-1] = 0;
3655 TRACE("returning %s\n", debugstr_a(str));
3656 return strlen(str);
3660 /*******************************************************************
3661 * GetMenuStringW (USER32.@)
3663 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3664 LPWSTR str, INT nMaxSiz, UINT wFlags )
3666 MENUITEM *item;
3668 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3669 if (str && nMaxSiz) str[0] = '\0';
3670 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3671 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3672 return 0;
3674 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3675 if( !(item->text)) {
3676 str[0] = 0;
3677 return 0;
3679 lstrcpynW( str, item->text, nMaxSiz );
3680 TRACE("returning %s\n", debugstr_w(str));
3681 return strlenW(str);
3685 /**********************************************************************
3686 * HiliteMenuItem (USER32.@)
3688 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3689 UINT wHilite )
3691 LPPOPUPMENU menu;
3692 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3693 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3694 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3695 if (menu->FocusedItem == wItemID) return TRUE;
3696 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3697 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3698 return TRUE;
3702 /**********************************************************************
3703 * GetMenuState (USER32.@)
3705 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3707 MENUITEM *item;
3708 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3709 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3710 debug_print_menuitem (" item: ", item, "");
3711 if (item->fType & MF_POPUP)
3713 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3714 if (!menu) return -1;
3715 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3717 else
3719 /* We used to (from way back then) mask the result to 0xff. */
3720 /* I don't know why and it seems wrong as the documented */
3721 /* return flag MF_SEPARATOR is outside that mask. */
3722 return (item->fType | item->fState);
3727 /**********************************************************************
3728 * GetMenuItemCount (USER32.@)
3730 INT WINAPI GetMenuItemCount( HMENU hMenu )
3732 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3733 if (!menu) return -1;
3734 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3735 return menu->nItems;
3739 /**********************************************************************
3740 * GetMenuItemID (USER32.@)
3742 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3744 MENUITEM * lpmi;
3746 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3747 if (lpmi->fType & MF_POPUP) return -1;
3748 return lpmi->wID;
3753 /*******************************************************************
3754 * InsertMenuW (USER32.@)
3756 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3757 UINT_PTR id, LPCWSTR str )
3759 MENUITEM *item;
3761 if (IS_STRING_ITEM(flags) && str)
3762 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3763 hMenu, pos, flags, id, debugstr_w(str) );
3764 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3765 hMenu, pos, flags, id, str );
3767 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3769 if (!(MENU_SetItemData( item, flags, id, str )))
3771 RemoveMenu( hMenu, pos, flags );
3772 return FALSE;
3775 item->hCheckBit = item->hUnCheckBit = 0;
3776 return TRUE;
3780 /*******************************************************************
3781 * InsertMenuA (USER32.@)
3783 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3784 UINT_PTR id, LPCSTR str )
3786 BOOL ret = FALSE;
3788 if (IS_STRING_ITEM(flags) && str)
3790 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3791 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3792 if (newstr)
3794 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3795 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3796 HeapFree( GetProcessHeap(), 0, newstr );
3798 return ret;
3800 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3804 /*******************************************************************
3805 * AppendMenuA (USER32.@)
3807 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3808 UINT_PTR id, LPCSTR data )
3810 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3814 /*******************************************************************
3815 * AppendMenuW (USER32.@)
3817 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3818 UINT_PTR id, LPCWSTR data )
3820 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3824 /**********************************************************************
3825 * RemoveMenu (USER32.@)
3827 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3829 LPPOPUPMENU menu;
3830 MENUITEM *item;
3832 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3833 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3834 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3836 /* Remove item */
3838 MENU_FreeItemData( item );
3840 if (--menu->nItems == 0)
3842 HeapFree( GetProcessHeap(), 0, menu->items );
3843 menu->items = NULL;
3845 else
3847 while(nPos < menu->nItems)
3849 *item = *(item+1);
3850 item++;
3851 nPos++;
3853 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3854 menu->nItems * sizeof(MENUITEM) );
3856 return TRUE;
3860 /**********************************************************************
3861 * DeleteMenu (USER32.@)
3863 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3865 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3866 if (!item) return FALSE;
3867 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3868 /* nPos is now the position of the item */
3869 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3870 return TRUE;
3874 /*******************************************************************
3875 * ModifyMenuW (USER32.@)
3877 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3878 UINT_PTR id, LPCWSTR str )
3880 MENUITEM *item;
3882 if (IS_STRING_ITEM(flags))
3883 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3884 else
3885 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3887 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3888 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3889 return MENU_SetItemData( item, flags, id, str );
3893 /*******************************************************************
3894 * ModifyMenuA (USER32.@)
3896 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3897 UINT_PTR id, LPCSTR str )
3899 BOOL ret = FALSE;
3901 if (IS_STRING_ITEM(flags) && str)
3903 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3904 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3905 if (newstr)
3907 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3908 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3909 HeapFree( GetProcessHeap(), 0, newstr );
3911 return ret;
3913 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3917 /**********************************************************************
3918 * CreatePopupMenu (USER32.@)
3920 HMENU WINAPI CreatePopupMenu(void)
3922 HMENU hmenu;
3923 POPUPMENU *menu;
3925 if (!(hmenu = CreateMenu())) return 0;
3926 menu = MENU_GetMenu( hmenu );
3927 menu->wFlags |= MF_POPUP;
3928 menu->bTimeToHide = FALSE;
3929 return hmenu;
3933 /**********************************************************************
3934 * GetMenuCheckMarkDimensions (USER.417)
3935 * GetMenuCheckMarkDimensions (USER32.@)
3937 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3939 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3943 /**********************************************************************
3944 * SetMenuItemBitmaps (USER32.@)
3946 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3947 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3949 MENUITEM *item;
3951 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3953 if (!hNewCheck && !hNewUnCheck)
3955 item->fState &= ~MF_USECHECKBITMAPS;
3957 else /* Install new bitmaps */
3959 item->hCheckBit = hNewCheck;
3960 item->hUnCheckBit = hNewUnCheck;
3961 item->fState |= MF_USECHECKBITMAPS;
3963 return TRUE;
3967 /**********************************************************************
3968 * CreateMenu (USER32.@)
3970 HMENU WINAPI CreateMenu(void)
3972 HMENU hMenu;
3973 LPPOPUPMENU menu;
3974 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3975 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3977 ZeroMemory(menu, sizeof(POPUPMENU));
3978 menu->wMagic = MENU_MAGIC;
3979 menu->FocusedItem = NO_SELECTED_ITEM;
3980 menu->bTimeToHide = FALSE;
3982 TRACE("return %p\n", hMenu );
3984 return hMenu;
3988 /**********************************************************************
3989 * DestroyMenu (USER32.@)
3991 BOOL WINAPI DestroyMenu( HMENU hMenu )
3993 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3995 TRACE("(%p)\n", hMenu);
3998 if (!lppop) return FALSE;
4000 lppop->wMagic = 0; /* Mark it as destroyed */
4002 /* DestroyMenu should not destroy system menu popup owner */
4003 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4005 DestroyWindow( lppop->hWnd );
4006 lppop->hWnd = 0;
4009 if (lppop->items) /* recursively destroy submenus */
4011 int i;
4012 MENUITEM *item = lppop->items;
4013 for (i = lppop->nItems; i > 0; i--, item++)
4015 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4016 MENU_FreeItemData( item );
4018 HeapFree( GetProcessHeap(), 0, lppop->items );
4020 USER_HEAP_FREE( hMenu );
4021 return TRUE;
4025 /**********************************************************************
4026 * GetSystemMenu (USER32.@)
4028 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4030 WND *wndPtr = WIN_GetPtr( hWnd );
4031 HMENU retvalue = 0;
4033 if (wndPtr == WND_DESKTOP) return 0;
4034 if (wndPtr == WND_OTHER_PROCESS)
4036 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4038 else if (wndPtr)
4040 if (wndPtr->hSysMenu && bRevert)
4042 DestroyMenu(wndPtr->hSysMenu);
4043 wndPtr->hSysMenu = 0;
4046 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4047 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4049 if( wndPtr->hSysMenu )
4051 POPUPMENU *menu;
4052 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4054 /* Store the dummy sysmenu handle to facilitate the refresh */
4055 /* of the close button if the SC_CLOSE item change */
4056 menu = MENU_GetMenu(retvalue);
4057 if ( menu )
4058 menu->hSysMenuOwner = wndPtr->hSysMenu;
4060 WIN_ReleasePtr( wndPtr );
4062 return bRevert ? 0 : retvalue;
4066 /*******************************************************************
4067 * SetSystemMenu (USER32.@)
4069 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4071 WND *wndPtr = WIN_GetPtr( hwnd );
4073 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4075 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4076 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4077 WIN_ReleasePtr( wndPtr );
4078 return TRUE;
4080 return FALSE;
4084 /**********************************************************************
4085 * GetMenu (USER32.@)
4087 HMENU WINAPI GetMenu( HWND hWnd )
4089 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4090 TRACE("for %p returning %p\n", hWnd, retvalue);
4091 return retvalue;
4094 /**********************************************************************
4095 * GetMenuBarInfo (USER32.@)
4097 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4099 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4100 return FALSE;
4103 /**********************************************************************
4104 * MENU_SetMenu
4106 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4107 * SetWindowPos call that would result if SetMenu were called directly.
4109 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4111 TRACE("(%p, %p);\n", hWnd, hMenu);
4113 if (hMenu && !IsMenu(hMenu))
4115 WARN("hMenu %p is not a menu handle\n", hMenu);
4116 return FALSE;
4118 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4119 return FALSE;
4121 hWnd = WIN_GetFullHandle( hWnd );
4122 if (GetCapture() == hWnd)
4123 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4125 if (hMenu != 0)
4127 LPPOPUPMENU lpmenu;
4129 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4131 lpmenu->hWnd = hWnd;
4132 lpmenu->Height = 0; /* Make sure we recalculate the size */
4134 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4135 return TRUE;
4139 /**********************************************************************
4140 * SetMenu (USER32.@)
4142 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4144 if(!MENU_SetMenu(hWnd, hMenu))
4145 return FALSE;
4147 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4148 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4149 return TRUE;
4153 /**********************************************************************
4154 * GetSubMenu (USER32.@)
4156 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4158 MENUITEM * lpmi;
4160 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4161 if (!(lpmi->fType & MF_POPUP)) return 0;
4162 return lpmi->hSubMenu;
4166 /**********************************************************************
4167 * DrawMenuBar (USER32.@)
4169 BOOL WINAPI DrawMenuBar( HWND hWnd )
4171 LPPOPUPMENU lppop;
4172 HMENU hMenu = GetMenu(hWnd);
4174 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4175 return FALSE;
4176 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4178 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4179 lppop->hwndOwner = hWnd;
4180 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4181 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4182 return TRUE;
4185 /***********************************************************************
4186 * DrawMenuBarTemp (USER32.@)
4188 * UNDOCUMENTED !!
4190 * called by W98SE desk.cpl Control Panel Applet
4192 * Not 100% sure about the param names, but close.
4194 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4196 LPPOPUPMENU lppop;
4197 UINT i,retvalue;
4198 HFONT hfontOld = 0;
4199 BOOL flat_menu = FALSE;
4201 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4203 if (!hMenu)
4204 hMenu = GetMenu(hwnd);
4206 if (!hFont)
4207 hFont = get_menu_font(FALSE);
4209 lppop = MENU_GetMenu( hMenu );
4210 if (lppop == NULL || lprect == NULL)
4212 retvalue = GetSystemMetrics(SM_CYMENU);
4213 goto END;
4216 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4218 hfontOld = SelectObject( hDC, hFont);
4220 if (lppop->Height == 0)
4221 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4223 lprect->bottom = lprect->top + lppop->Height;
4225 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4227 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4228 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4229 LineTo( hDC, lprect->right, lprect->bottom );
4231 if (lppop->nItems == 0)
4233 retvalue = GetSystemMetrics(SM_CYMENU);
4234 goto END;
4237 for (i = 0; i < lppop->nItems; i++)
4239 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4240 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4242 retvalue = lppop->Height;
4244 END:
4245 if (hfontOld) SelectObject (hDC, hfontOld);
4246 return retvalue;
4249 /***********************************************************************
4250 * EndMenu (USER.187)
4251 * EndMenu (USER32.@)
4253 BOOL WINAPI EndMenu(void)
4255 /* if we are in the menu code, and it is active */
4256 if (!fEndMenu && top_popup)
4258 /* terminate the menu handling code */
4259 fEndMenu = TRUE;
4261 /* needs to be posted to wakeup the internal menu handler */
4262 /* which will now terminate the menu, in the event that */
4263 /* the main window was minimized, or lost focus, so we */
4264 /* don't end up with an orphaned menu */
4265 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4267 return fEndMenu;
4271 /***********************************************************************
4272 * LookupMenuHandle (USER.217)
4274 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4276 HMENU hmenu32 = HMENU_32(hmenu);
4277 UINT id32 = id;
4278 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4279 else return HMENU_16(hmenu32);
4283 /**********************************************************************
4284 * LoadMenu (USER.150)
4286 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4288 HRSRC16 hRsrc;
4289 HGLOBAL16 handle;
4290 HMENU16 hMenu;
4292 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4293 if (!name) return 0;
4295 instance = GetExePtr( instance );
4296 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4297 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4298 hMenu = LoadMenuIndirect16(LockResource16(handle));
4299 FreeResource16( handle );
4300 return hMenu;
4304 /*****************************************************************
4305 * LoadMenuA (USER32.@)
4307 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4309 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4310 if (!hrsrc) return 0;
4311 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4315 /*****************************************************************
4316 * LoadMenuW (USER32.@)
4318 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4320 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4321 if (!hrsrc) return 0;
4322 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4326 /**********************************************************************
4327 * LoadMenuIndirect (USER.220)
4329 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4331 HMENU hMenu;
4332 WORD version, offset;
4333 LPCSTR p = (LPCSTR)template;
4335 TRACE("(%p)\n", template );
4336 version = GET_WORD(p);
4337 p += sizeof(WORD);
4338 if (version)
4340 WARN("version must be 0 for Win16\n" );
4341 return 0;
4343 offset = GET_WORD(p);
4344 p += sizeof(WORD) + offset;
4345 if (!(hMenu = CreateMenu())) return 0;
4346 if (!MENU_ParseResource( p, hMenu, FALSE ))
4348 DestroyMenu( hMenu );
4349 return 0;
4351 return HMENU_16(hMenu);
4355 /**********************************************************************
4356 * LoadMenuIndirectW (USER32.@)
4358 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4360 HMENU hMenu;
4361 WORD version, offset;
4362 LPCSTR p = (LPCSTR)template;
4364 version = GET_WORD(p);
4365 p += sizeof(WORD);
4366 TRACE("%p, ver %d\n", template, version );
4367 switch (version)
4369 case 0: /* standard format is version of 0 */
4370 offset = GET_WORD(p);
4371 p += sizeof(WORD) + offset;
4372 if (!(hMenu = CreateMenu())) return 0;
4373 if (!MENU_ParseResource( p, hMenu, TRUE ))
4375 DestroyMenu( hMenu );
4376 return 0;
4378 return hMenu;
4379 case 1: /* extended format is version of 1 */
4380 offset = GET_WORD(p);
4381 p += sizeof(WORD) + offset;
4382 if (!(hMenu = CreateMenu())) return 0;
4383 if (!MENUEX_ParseResource( p, hMenu))
4385 DestroyMenu( hMenu );
4386 return 0;
4388 return hMenu;
4389 default:
4390 ERR("version %d not supported.\n", version);
4391 return 0;
4396 /**********************************************************************
4397 * LoadMenuIndirectA (USER32.@)
4399 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4401 return LoadMenuIndirectW( template );
4405 /**********************************************************************
4406 * IsMenu (USER32.@)
4408 BOOL WINAPI IsMenu(HMENU hmenu)
4410 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4412 if (!menu)
4414 SetLastError(ERROR_INVALID_MENU_HANDLE);
4415 return FALSE;
4417 return TRUE;
4420 /**********************************************************************
4421 * GetMenuItemInfo_common
4424 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4425 LPMENUITEMINFOW lpmii, BOOL unicode)
4427 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4429 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4431 if (!menu) {
4432 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4433 return FALSE;
4436 if( lpmii->fMask & MIIM_TYPE) {
4437 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4438 WARN("invalid combination of fMask bits used\n");
4439 /* this does not happen on Win9x/ME */
4440 SetLastError( ERROR_INVALID_PARAMETER);
4441 return FALSE;
4443 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4444 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4445 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4446 if( lpmii->fType & MFT_BITMAP) {
4447 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4448 lpmii->cch = 0;
4449 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4450 /* this does not happen on Win9x/ME */
4451 lpmii->dwTypeData = 0;
4452 lpmii->cch = 0;
4456 /* copy the text string */
4457 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4458 if( !menu->text ) {
4459 if(lpmii->dwTypeData && lpmii->cch) {
4460 lpmii->cch = 0;
4461 if( unicode)
4462 *((WCHAR *)lpmii->dwTypeData) = 0;
4463 else
4464 *((CHAR *)lpmii->dwTypeData) = 0;
4466 } else {
4467 int len;
4468 if (unicode)
4470 len = strlenW(menu->text);
4471 if(lpmii->dwTypeData && lpmii->cch)
4472 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4474 else
4476 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4477 0, NULL, NULL ) - 1;
4478 if(lpmii->dwTypeData && lpmii->cch)
4479 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4480 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4481 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4483 /* if we've copied a substring we return its length */
4484 if(lpmii->dwTypeData && lpmii->cch)
4485 if (lpmii->cch <= len + 1)
4486 lpmii->cch--;
4487 else
4488 lpmii->cch = len;
4489 else {
4490 /* return length of string */
4491 /* not on Win9x/ME if fType & MFT_BITMAP */
4492 lpmii->cch = len;
4497 if (lpmii->fMask & MIIM_FTYPE)
4498 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4500 if (lpmii->fMask & MIIM_BITMAP)
4501 lpmii->hbmpItem = menu->hbmpItem;
4503 if (lpmii->fMask & MIIM_STATE)
4504 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4506 if (lpmii->fMask & MIIM_ID)
4507 lpmii->wID = menu->wID;
4509 if (lpmii->fMask & MIIM_SUBMENU)
4510 lpmii->hSubMenu = menu->hSubMenu;
4511 else {
4512 /* hSubMenu is always cleared
4513 * (not on Win9x/ME ) */
4514 lpmii->hSubMenu = 0;
4517 if (lpmii->fMask & MIIM_CHECKMARKS) {
4518 lpmii->hbmpChecked = menu->hCheckBit;
4519 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4521 if (lpmii->fMask & MIIM_DATA)
4522 lpmii->dwItemData = menu->dwItemData;
4524 return TRUE;
4527 /**********************************************************************
4528 * GetMenuItemInfoA (USER32.@)
4530 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4531 LPMENUITEMINFOA lpmii)
4533 BOOL ret;
4534 MENUITEMINFOA mii;
4535 if( lpmii->cbSize != sizeof( mii) &&
4536 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4537 SetLastError( ERROR_INVALID_PARAMETER);
4538 return FALSE;
4540 memcpy( &mii, lpmii, lpmii->cbSize);
4541 mii.cbSize = sizeof( mii);
4542 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4543 (LPMENUITEMINFOW)&mii, FALSE);
4544 mii.cbSize = lpmii->cbSize;
4545 memcpy( lpmii, &mii, mii.cbSize);
4546 return ret;
4549 /**********************************************************************
4550 * GetMenuItemInfoW (USER32.@)
4552 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4553 LPMENUITEMINFOW lpmii)
4555 BOOL ret;
4556 MENUITEMINFOW mii;
4557 if( lpmii->cbSize != sizeof( mii) &&
4558 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4559 SetLastError( ERROR_INVALID_PARAMETER);
4560 return FALSE;
4562 memcpy( &mii, lpmii, lpmii->cbSize);
4563 mii.cbSize = sizeof( mii);
4564 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4565 mii.cbSize = lpmii->cbSize;
4566 memcpy( lpmii, &mii, mii.cbSize);
4567 return ret;
4571 /* set a menu item text from a ASCII or Unicode string */
4572 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4574 if (!text)
4575 menu->text = NULL;
4576 else if (unicode)
4578 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4579 strcpyW( menu->text, text );
4581 else
4583 LPCSTR str = (LPCSTR)text;
4584 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4585 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4586 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4591 /**********************************************************************
4592 * SetMenuItemInfo_common
4595 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4596 const MENUITEMINFOW *lpmii,
4597 BOOL unicode)
4599 if (!menu) return FALSE;
4601 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4603 if (lpmii->fMask & MIIM_TYPE ) {
4604 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4605 WARN("invalid combination of fMask bits used\n");
4606 /* this does not happen on Win9x/ME */
4607 SetLastError( ERROR_INVALID_PARAMETER);
4608 return FALSE;
4611 /* Remove the old type bits and replace them with the new ones */
4612 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4613 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4615 if (IS_STRING_ITEM(menu->fType)) {
4616 HeapFree(GetProcessHeap(), 0, menu->text);
4617 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4618 } else if( (menu->fType) & MFT_BITMAP)
4619 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4622 if (lpmii->fMask & MIIM_FTYPE ) {
4623 if(( lpmii->fType & MFT_BITMAP)) {
4624 SetLastError( ERROR_INVALID_PARAMETER);
4625 return FALSE;
4627 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4628 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4630 if (lpmii->fMask & MIIM_STRING ) {
4631 /* free the string when used */
4632 HeapFree(GetProcessHeap(), 0, menu->text);
4633 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4636 if (lpmii->fMask & MIIM_STATE)
4638 /* Other menu items having MFS_DEFAULT are not converted
4639 to normal items */
4640 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4643 if (lpmii->fMask & MIIM_ID)
4644 menu->wID = lpmii->wID;
4646 if (lpmii->fMask & MIIM_SUBMENU) {
4647 menu->hSubMenu = lpmii->hSubMenu;
4648 if (menu->hSubMenu) {
4649 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4650 if (subMenu) {
4651 subMenu->wFlags |= MF_POPUP;
4652 menu->fType |= MF_POPUP;
4654 else {
4655 SetLastError( ERROR_INVALID_PARAMETER);
4656 return FALSE;
4659 else
4660 menu->fType &= ~MF_POPUP;
4663 if (lpmii->fMask & MIIM_CHECKMARKS)
4665 menu->hCheckBit = lpmii->hbmpChecked;
4666 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4668 if (lpmii->fMask & MIIM_DATA)
4669 menu->dwItemData = lpmii->dwItemData;
4671 if (lpmii->fMask & MIIM_BITMAP)
4672 menu->hbmpItem = lpmii->hbmpItem;
4674 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4675 menu->fType |= MFT_SEPARATOR;
4677 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4678 return TRUE;
4681 /**********************************************************************
4682 * SetMenuItemInfoA (USER32.@)
4684 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4685 const MENUITEMINFOA *lpmii)
4687 MENUITEMINFOA mii;
4689 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4691 if( lpmii->cbSize != sizeof( mii) &&
4692 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4693 SetLastError( ERROR_INVALID_PARAMETER);
4694 return FALSE;
4696 memcpy( &mii, lpmii, lpmii->cbSize);
4697 if( lpmii->cbSize != sizeof( mii)) {
4698 mii.cbSize = sizeof( mii);
4699 mii.hbmpItem = NULL;
4701 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4702 (const MENUITEMINFOW *)&mii, FALSE);
4705 /**********************************************************************
4706 * SetMenuItemInfoW (USER32.@)
4708 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4709 const MENUITEMINFOW *lpmii)
4711 MENUITEMINFOW mii;
4713 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4715 if( lpmii->cbSize != sizeof( mii) &&
4716 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4717 SetLastError( ERROR_INVALID_PARAMETER);
4718 return FALSE;
4720 memcpy( &mii, lpmii, lpmii->cbSize);
4721 if( lpmii->cbSize != sizeof( mii)) {
4722 mii.cbSize = sizeof( mii);
4723 mii.hbmpItem = NULL;
4725 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4726 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4729 /**********************************************************************
4730 * SetMenuDefaultItem (USER32.@)
4733 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4735 UINT i;
4736 POPUPMENU *menu;
4737 MENUITEM *item;
4739 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4741 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4743 /* reset all default-item flags */
4744 item = menu->items;
4745 for (i = 0; i < menu->nItems; i++, item++)
4747 item->fState &= ~MFS_DEFAULT;
4750 /* no default item */
4751 if ( -1 == uItem)
4753 return TRUE;
4756 item = menu->items;
4757 if ( bypos )
4759 if ( uItem >= menu->nItems ) return FALSE;
4760 item[uItem].fState |= MFS_DEFAULT;
4761 return TRUE;
4763 else
4765 for (i = 0; i < menu->nItems; i++, item++)
4767 if (item->wID == uItem)
4769 item->fState |= MFS_DEFAULT;
4770 return TRUE;
4775 return FALSE;
4778 /**********************************************************************
4779 * GetMenuDefaultItem (USER32.@)
4781 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4783 POPUPMENU *menu;
4784 MENUITEM * item;
4785 UINT i = 0;
4787 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4789 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4791 /* find default item */
4792 item = menu->items;
4794 /* empty menu */
4795 if (! item) return -1;
4797 while ( !( item->fState & MFS_DEFAULT ) )
4799 i++; item++;
4800 if (i >= menu->nItems ) return -1;
4803 /* default: don't return disabled items */
4804 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4806 /* search rekursiv when needed */
4807 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4809 UINT ret;
4810 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4811 if ( -1 != ret ) return ret;
4813 /* when item not found in submenu, return the popup item */
4815 return ( bypos ) ? i : item->wID;
4820 /**********************************************************************
4821 * InsertMenuItemA (USER32.@)
4823 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4824 const MENUITEMINFOA *lpmii)
4826 MENUITEM *item;
4827 MENUITEMINFOA mii;
4829 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4831 if( lpmii->cbSize != sizeof( mii) &&
4832 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4833 SetLastError( ERROR_INVALID_PARAMETER);
4834 return FALSE;
4836 memcpy( &mii, lpmii, lpmii->cbSize);
4837 if( lpmii->cbSize != sizeof( mii)) {
4838 mii.cbSize = sizeof( mii);
4839 mii.hbmpItem = NULL;
4842 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4843 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4847 /**********************************************************************
4848 * InsertMenuItemW (USER32.@)
4850 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4851 const MENUITEMINFOW *lpmii)
4853 MENUITEM *item;
4854 MENUITEMINFOW mii;
4856 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4858 if( lpmii->cbSize != sizeof( mii) &&
4859 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4860 SetLastError( ERROR_INVALID_PARAMETER);
4861 return FALSE;
4863 memcpy( &mii, lpmii, lpmii->cbSize);
4864 if( lpmii->cbSize != sizeof( mii)) {
4865 mii.cbSize = sizeof( mii);
4866 mii.hbmpItem = NULL;
4869 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4870 return SetMenuItemInfo_common(item, &mii, TRUE);
4873 /**********************************************************************
4874 * CheckMenuRadioItem (USER32.@)
4877 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4878 UINT first, UINT last, UINT check,
4879 UINT bypos)
4881 BOOL done = FALSE;
4882 UINT i;
4883 MENUITEM *mi_first = NULL, *mi_check;
4884 HMENU m_first, m_check;
4886 for (i = first; i <= last; i++)
4888 UINT pos = i;
4890 if (!mi_first)
4892 m_first = hMenu;
4893 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4894 if (!mi_first) continue;
4895 mi_check = mi_first;
4896 m_check = m_first;
4898 else
4900 m_check = hMenu;
4901 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4902 if (!mi_check) continue;
4905 if (m_first != m_check) continue;
4906 if (mi_check->fType == MFT_SEPARATOR) continue;
4908 if (i == check)
4910 mi_check->fType |= MFT_RADIOCHECK;
4911 mi_check->fState |= MFS_CHECKED;
4912 done = TRUE;
4914 else
4916 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4917 mi_check->fState &= ~MFS_CHECKED;
4921 return done;
4925 /**********************************************************************
4926 * GetMenuItemRect (USER32.@)
4928 * ATTENTION: Here, the returned values in rect are the screen
4929 * coordinates of the item just like if the menu was
4930 * always on the upper left side of the application.
4933 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4934 LPRECT rect)
4936 POPUPMENU *itemMenu;
4937 MENUITEM *item;
4938 HWND referenceHwnd;
4940 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4942 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4943 referenceHwnd = hwnd;
4945 if(!hwnd)
4947 itemMenu = MENU_GetMenu(hMenu);
4948 if (itemMenu == NULL)
4949 return FALSE;
4951 if(itemMenu->hWnd == 0)
4952 return FALSE;
4953 referenceHwnd = itemMenu->hWnd;
4956 if ((rect == NULL) || (item == NULL))
4957 return FALSE;
4959 *rect = item->rect;
4961 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4963 return TRUE;
4967 /**********************************************************************
4968 * SetMenuInfo (USER32.@)
4970 * FIXME
4971 * MIM_APPLYTOSUBMENUS
4972 * actually use the items to draw the menu
4974 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4976 POPUPMENU *menu;
4978 TRACE("(%p %p)\n", hMenu, lpmi);
4980 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4983 if (lpmi->fMask & MIM_BACKGROUND)
4984 menu->hbrBack = lpmi->hbrBack;
4986 if (lpmi->fMask & MIM_HELPID)
4987 menu->dwContextHelpID = lpmi->dwContextHelpID;
4989 if (lpmi->fMask & MIM_MAXHEIGHT)
4990 menu->cyMax = lpmi->cyMax;
4992 if (lpmi->fMask & MIM_MENUDATA)
4993 menu->dwMenuData = lpmi->dwMenuData;
4995 if (lpmi->fMask & MIM_STYLE)
4997 menu->dwStyle = lpmi->dwStyle;
4998 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4999 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5000 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5003 return TRUE;
5005 return FALSE;
5008 /**********************************************************************
5009 * GetMenuInfo (USER32.@)
5011 * NOTES
5012 * win98/NT5.0
5015 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5016 { POPUPMENU *menu;
5018 TRACE("(%p %p)\n", hMenu, lpmi);
5020 if (lpmi && (menu = MENU_GetMenu(hMenu)))
5023 if (lpmi->fMask & MIM_BACKGROUND)
5024 lpmi->hbrBack = menu->hbrBack;
5026 if (lpmi->fMask & MIM_HELPID)
5027 lpmi->dwContextHelpID = menu->dwContextHelpID;
5029 if (lpmi->fMask & MIM_MAXHEIGHT)
5030 lpmi->cyMax = menu->cyMax;
5032 if (lpmi->fMask & MIM_MENUDATA)
5033 lpmi->dwMenuData = menu->dwMenuData;
5035 if (lpmi->fMask & MIM_STYLE)
5036 lpmi->dwStyle = menu->dwStyle;
5038 return TRUE;
5040 return FALSE;
5044 /**********************************************************************
5045 * SetMenuContextHelpId (USER32.@)
5047 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5049 LPPOPUPMENU menu;
5051 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5053 if ((menu = MENU_GetMenu(hMenu)))
5055 menu->dwContextHelpID = dwContextHelpID;
5056 return TRUE;
5058 return FALSE;
5062 /**********************************************************************
5063 * GetMenuContextHelpId (USER32.@)
5065 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5067 LPPOPUPMENU menu;
5069 TRACE("(%p)\n", hMenu);
5071 if ((menu = MENU_GetMenu(hMenu)))
5073 return menu->dwContextHelpID;
5075 return 0;
5078 /**********************************************************************
5079 * MenuItemFromPoint (USER32.@)
5081 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5083 POPUPMENU *menu = MENU_GetMenu(hMenu);
5084 UINT pos;
5086 /*FIXME: Do we have to handle hWnd here? */
5087 if (!menu) return -1;
5088 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5089 return pos;
5093 /**********************************************************************
5094 * translate_accelerator
5096 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5097 BYTE fVirt, WORD key, WORD cmd )
5099 INT mask = 0;
5100 UINT mesg = 0;
5102 if (wParam != key) return FALSE;
5104 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5105 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5106 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5108 if (message == WM_CHAR || message == WM_SYSCHAR)
5110 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5112 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5113 goto found;
5116 else
5118 if(fVirt & FVIRTKEY)
5120 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5121 wParam, 0xff & HIWORD(lParam));
5123 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5124 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5126 else
5128 if (!(lParam & 0x01000000)) /* no special_key */
5130 if ((fVirt & FALT) && (lParam & 0x20000000))
5131 { /* ^^ ALT pressed */
5132 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5133 goto found;
5138 return FALSE;
5140 found:
5141 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5142 mesg = 1;
5143 else
5145 HMENU hMenu, hSubMenu, hSysMenu;
5146 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5148 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5149 hSysMenu = get_win_sys_menu( hWnd );
5151 /* find menu item and ask application to initialize it */
5152 /* 1. in the system menu */
5153 hSubMenu = hSysMenu;
5154 nPos = cmd;
5155 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5157 if (GetCapture())
5158 mesg = 2;
5159 if (!IsWindowEnabled(hWnd))
5160 mesg = 3;
5161 else
5163 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5164 if(hSubMenu != hSysMenu)
5166 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5167 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5168 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5170 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5173 else /* 2. in the window's menu */
5175 hSubMenu = hMenu;
5176 nPos = cmd;
5177 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5179 if (GetCapture())
5180 mesg = 2;
5181 if (!IsWindowEnabled(hWnd))
5182 mesg = 3;
5183 else
5185 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5186 if(hSubMenu != hMenu)
5188 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5189 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5190 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5192 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5197 if (mesg == 0)
5199 if (uSysStat != (UINT)-1)
5201 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5202 mesg=4;
5203 else
5204 mesg=WM_SYSCOMMAND;
5206 else
5208 if (uStat != (UINT)-1)
5210 if (IsIconic(hWnd))
5211 mesg=5;
5212 else
5214 if (uStat & (MF_DISABLED|MF_GRAYED))
5215 mesg=6;
5216 else
5217 mesg=WM_COMMAND;
5220 else
5221 mesg=WM_COMMAND;
5226 if( mesg==WM_COMMAND )
5228 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5229 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5231 else if( mesg==WM_SYSCOMMAND )
5233 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5234 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5236 else
5238 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5239 * #0: unknown (please report!)
5240 * #1: for WM_KEYUP,WM_SYSKEYUP
5241 * #2: mouse is captured
5242 * #3: window is disabled
5243 * #4: it's a disabled system menu option
5244 * #5: it's a menu option, but window is iconic
5245 * #6: it's a menu option, but disabled
5247 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5248 if(mesg==0)
5249 ERR_(accel)(" unknown reason - please report!\n");
5251 return TRUE;
5254 /**********************************************************************
5255 * TranslateAcceleratorA (USER32.@)
5256 * TranslateAccelerator (USER32.@)
5258 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5260 /* YES, Accel16! */
5261 LPACCEL16 lpAccelTbl;
5262 int i;
5263 WPARAM wParam;
5265 if (!hWnd || !msg) return 0;
5267 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5269 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5270 return 0;
5273 wParam = msg->wParam;
5275 switch (msg->message)
5277 case WM_KEYDOWN:
5278 case WM_SYSKEYDOWN:
5279 break;
5281 case WM_CHAR:
5282 case WM_SYSCHAR:
5284 char ch = LOWORD(wParam);
5285 WCHAR wch;
5286 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5287 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5289 break;
5291 default:
5292 return 0;
5295 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5296 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5297 i = 0;
5300 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5301 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5302 return 1;
5303 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5305 return 0;
5308 /**********************************************************************
5309 * TranslateAcceleratorW (USER32.@)
5311 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5313 /* YES, Accel16! */
5314 LPACCEL16 lpAccelTbl;
5315 int i;
5317 if (!hWnd || !msg) return 0;
5319 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5321 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5322 return 0;
5325 switch (msg->message)
5327 case WM_KEYDOWN:
5328 case WM_SYSKEYDOWN:
5329 case WM_CHAR:
5330 case WM_SYSCHAR:
5331 break;
5333 default:
5334 return 0;
5337 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5338 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5339 i = 0;
5342 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5343 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5344 return 1;
5345 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5347 return 0;