ntdll: move relocations from mapping into loader
[wine/kumbayo.git] / dlls / user32 / menu.c
blob9dc8b11e4642196bafa3c14020a0be55111043f6
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
56 #include "wownt32.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "win.h"
60 #include "controls.h"
61 #include "user_private.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(menu);
65 WINE_DECLARE_DEBUG_CHANNEL(accel);
67 /* Menu item structure */
68 typedef struct {
69 /* ----------- MENUITEMINFO Stuff ----------- */
70 UINT fType; /* Item type. */
71 UINT fState; /* Item state. */
72 UINT_PTR wID; /* Item id. */
73 HMENU hSubMenu; /* Pop-up menu. */
74 HBITMAP hCheckBit; /* Bitmap when checked. */
75 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
76 LPWSTR text; /* Item text. */
77 ULONG_PTR dwItemData; /* Application defined. */
78 LPWSTR dwTypeData; /* depends on fMask */
79 HBITMAP hbmpItem; /* bitmap */
80 /* ----------- Wine stuff ----------- */
81 RECT rect; /* Item area (relative to menu window) */
82 UINT xTab; /* X position of text after Tab */
83 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
84 * bitmap */
85 } MENUITEM;
87 /* Popup menu structure */
88 typedef struct {
89 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
90 WORD wMagic; /* Magic number */
91 WORD Width; /* Width of the whole menu */
92 WORD Height; /* Height of the whole menu */
93 UINT nItems; /* Number of items in the menu */
94 HWND hWnd; /* Window containing the menu */
95 MENUITEM *items; /* Array of menu items */
96 UINT FocusedItem; /* Currently focused item */
97 HWND hwndOwner; /* window receiving the messages for ownerdraw */
98 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
99 BOOL bScrolling; /* Scroll arrows are active */
100 UINT nScrollPos; /* Current scroll position */
101 UINT nTotalHeight; /* Total height of menu items inside menu */
102 /* ------------ MENUINFO members ------ */
103 DWORD dwStyle; /* Extended menu style */
104 UINT cyMax; /* max height of the whole menu, 0 is screen height */
105 HBRUSH hbrBack; /* brush for menu background */
106 DWORD dwContextHelpID;
107 DWORD dwMenuData; /* application defined value */
108 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
109 SIZE maxBmpSize; /* Maximum size of the bitmap items */
110 } POPUPMENU, *LPPOPUPMENU;
112 /* internal flags for menu tracking */
114 #define TF_ENDMENU 0x0001
115 #define TF_SUSPENDPOPUP 0x0002
116 #define TF_SKIPREMOVE 0x0004
118 typedef struct
120 UINT trackFlags;
121 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
122 HMENU hTopMenu; /* initial menu */
123 HWND hOwnerWnd; /* where notifications are sent */
124 POINT pt;
125 } MTRACKER;
127 #define MENU_MAGIC 0x554d /* 'MU' */
129 #define ITEM_PREV -1
130 #define ITEM_NEXT 1
132 /* Internal MENU_TrackMenu() flags */
133 #define TPM_INTERNAL 0xF0000000
134 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
135 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
136 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
138 /* Space between 2 columns */
139 #define MENU_COL_SPACE 4
141 /* top and bottom margins for popup menus */
142 #define MENU_TOP_MARGIN 3
143 #define MENU_BOTTOM_MARGIN 2
145 /* (other menu->FocusedItem values give the position of the focused item) */
146 #define NO_SELECTED_ITEM 0xffff
148 #define MENU_ITEM_TYPE(flags) \
149 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
151 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
152 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
153 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
155 #define IS_SYSTEM_MENU(menu) \
156 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
158 #define MENUITEMINFO_TYPE_MASK \
159 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
160 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
161 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
162 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
163 #define STATE_MASK (~TYPE_MASK)
164 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
166 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
168 static SIZE menucharsize;
169 static UINT ODitemheight; /* default owner drawn item height */
171 /* Use global popup window because there's no way 2 menus can
172 * be tracked at the same time. */
173 static HWND top_popup;
175 /* Flag set by EndMenu() to force an exit from menu tracking */
176 static BOOL fEndMenu = FALSE;
178 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
180 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
182 /*********************************************************************
183 * menu class descriptor
185 const struct builtin_class_descr MENU_builtin_class =
187 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
188 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
189 NULL, /* procA (winproc is Unicode only) */
190 PopupMenuWndProc, /* procW */
191 sizeof(HMENU), /* extra */
192 0, /* cursor */
193 (HBRUSH)(COLOR_MENU+1) /* brush */
197 /***********************************************************************
198 * debug_print_menuitem
200 * Print a menuitem in readable form.
203 #define debug_print_menuitem(pre, mp, post) \
204 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
206 #define MENUOUT(text) \
207 TRACE("%s%s", (count++ ? "," : ""), (text))
209 #define MENUFLAG(bit,text) \
210 do { \
211 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
212 } while (0)
214 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
215 const char *postfix)
217 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
218 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
219 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
220 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
221 TRACE("%s ", prefix);
222 if (mp) {
223 UINT flags = mp->fType;
224 TRACE( "{ ID=0x%lx", mp->wID);
225 if ( mp->hSubMenu)
226 TRACE( ", Sub=%p", mp->hSubMenu);
227 if (flags) {
228 int count = 0;
229 TRACE( ", fType=");
230 MENUFLAG( MFT_SEPARATOR, "sep");
231 MENUFLAG( MFT_OWNERDRAW, "own");
232 MENUFLAG( MFT_BITMAP, "bit");
233 MENUFLAG(MF_POPUP, "pop");
234 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
235 MENUFLAG(MFT_MENUBREAK, "brk");
236 MENUFLAG(MFT_RADIOCHECK, "radio");
237 MENUFLAG(MFT_RIGHTORDER, "rorder");
238 MENUFLAG(MF_SYSMENU, "sys");
239 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
240 if (flags)
241 TRACE( "+0x%x", flags);
243 flags = mp->fState;
244 if (flags) {
245 int count = 0;
246 TRACE( ", State=");
247 MENUFLAG(MFS_GRAYED, "grey");
248 MENUFLAG(MFS_DEFAULT, "default");
249 MENUFLAG(MFS_DISABLED, "dis");
250 MENUFLAG(MFS_CHECKED, "check");
251 MENUFLAG(MFS_HILITE, "hi");
252 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
253 MENUFLAG(MF_MOUSESELECT, "mouse");
254 if (flags)
255 TRACE( "+0x%x", flags);
257 if (mp->hCheckBit)
258 TRACE( ", Chk=%p", mp->hCheckBit);
259 if (mp->hUnCheckBit)
260 TRACE( ", Unc=%p", mp->hUnCheckBit);
261 if (mp->text)
262 TRACE( ", Text=%s", debugstr_w(mp->text));
263 if (mp->dwItemData)
264 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
265 if (mp->hbmpItem)
267 if( IS_MAGIC_BITMAP(mp->hbmpItem))
268 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
269 else
270 TRACE( ", hbitmap=%p", mp->hbmpItem);
272 TRACE( " }");
273 } else
274 TRACE( "NULL");
275 TRACE(" %s\n", postfix);
278 #undef MENUOUT
279 #undef MENUFLAG
282 /***********************************************************************
283 * MENU_GetMenu
285 * Validate the given menu handle and returns the menu structure pointer.
287 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
289 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
290 if (!menu || menu->wMagic != MENU_MAGIC)
292 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
293 menu = NULL;
295 return menu;
298 /***********************************************************************
299 * get_win_sys_menu
301 * Get the system menu of a window
303 static HMENU get_win_sys_menu( HWND hwnd )
305 HMENU ret = 0;
306 WND *win = WIN_GetPtr( hwnd );
307 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
309 ret = win->hSysMenu;
310 WIN_ReleasePtr( win );
312 return ret;
315 /***********************************************************************
316 * get_menu_font
318 static HFONT get_menu_font( BOOL bold )
320 static HFONT hMenuFont, hMenuFontBold;
322 HFONT ret = bold ? hMenuFontBold : hMenuFont;
324 if (!ret)
326 NONCLIENTMETRICSW ncm;
327 HFONT prev;
329 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
330 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
332 if (bold)
334 ncm.lfMenuFont.lfWeight += 300;
335 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
337 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
338 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
339 ret, NULL );
340 if (prev)
342 /* another thread beat us to it */
343 DeleteObject( ret );
344 ret = prev;
347 return ret;
350 /***********************************************************************
351 * get_arrow_bitmap
353 static HBITMAP get_arrow_bitmap(void)
355 static HBITMAP arrow_bitmap;
357 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
358 return arrow_bitmap;
361 /***********************************************************************
362 * get_down_arrow_bitmap
364 static HBITMAP get_down_arrow_bitmap(void)
366 static HBITMAP arrow_bitmap;
368 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
369 return arrow_bitmap;
372 /***********************************************************************
373 * get_down_arrow_inactive_bitmap
375 static HBITMAP get_down_arrow_inactive_bitmap(void)
377 static HBITMAP arrow_bitmap;
379 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
380 return arrow_bitmap;
383 /***********************************************************************
384 * get_up_arrow_bitmap
386 static HBITMAP get_up_arrow_bitmap(void)
388 static HBITMAP arrow_bitmap;
390 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
391 return arrow_bitmap;
394 /***********************************************************************
395 * get_up_arrow_inactive_bitmap
397 static HBITMAP get_up_arrow_inactive_bitmap(void)
399 static HBITMAP arrow_bitmap;
401 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
402 return arrow_bitmap;
405 /***********************************************************************
406 * MENU_CopySysPopup
408 * Return the default system menu.
410 static HMENU MENU_CopySysPopup(void)
412 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
413 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
415 if( hMenu ) {
416 POPUPMENU* menu = MENU_GetMenu(hMenu);
417 menu->wFlags |= MF_SYSMENU | MF_POPUP;
418 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
420 else
421 ERR("Unable to load default system menu\n" );
423 TRACE("returning %p.\n", hMenu );
425 return hMenu;
429 /**********************************************************************
430 * MENU_GetSysMenu
432 * Create a copy of the system menu. System menu in Windows is
433 * a special menu bar with the single entry - system menu popup.
434 * This popup is presented to the outside world as a "system menu".
435 * However, the real system menu handle is sometimes seen in the
436 * WM_MENUSELECT parameters (and Word 6 likes it this way).
438 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
440 HMENU hMenu;
442 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
443 if ((hMenu = CreateMenu()))
445 POPUPMENU *menu = MENU_GetMenu(hMenu);
446 menu->wFlags = MF_SYSMENU;
447 menu->hWnd = WIN_GetFullHandle( hWnd );
448 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
450 if (!hPopupMenu)
451 hPopupMenu = MENU_CopySysPopup();
453 if (hPopupMenu)
455 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
456 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
458 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
459 (UINT_PTR)hPopupMenu, NULL );
461 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
462 menu->items[0].fState = 0;
463 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
465 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
466 return hMenu;
468 DestroyMenu( hMenu );
470 ERR("failed to load system menu!\n");
471 return 0;
475 /***********************************************************************
476 * MENU_InitSysMenuPopup
478 * Grey the appropriate items in System menu.
480 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
482 BOOL gray;
484 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
485 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
486 gray = ((style & WS_MAXIMIZE) != 0);
487 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
488 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
489 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
490 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
491 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
492 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
493 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
494 gray = (clsStyle & CS_NOCLOSE) != 0;
496 /* The menu item must keep its state if it's disabled */
497 if(gray)
498 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
502 /******************************************************************************
504 * UINT MENU_GetStartOfNextColumn(
505 * HMENU hMenu )
507 *****************************************************************************/
509 static UINT MENU_GetStartOfNextColumn(
510 HMENU hMenu )
512 POPUPMENU *menu = MENU_GetMenu(hMenu);
513 UINT i;
515 if(!menu)
516 return NO_SELECTED_ITEM;
518 i = menu->FocusedItem + 1;
519 if( i == NO_SELECTED_ITEM )
520 return i;
522 for( ; i < menu->nItems; ++i ) {
523 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
524 return i;
527 return NO_SELECTED_ITEM;
531 /******************************************************************************
533 * UINT MENU_GetStartOfPrevColumn(
534 * HMENU hMenu )
536 *****************************************************************************/
538 static UINT MENU_GetStartOfPrevColumn(
539 HMENU hMenu )
541 POPUPMENU *menu = MENU_GetMenu(hMenu);
542 UINT i;
544 if( !menu )
545 return NO_SELECTED_ITEM;
547 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
548 return NO_SELECTED_ITEM;
550 /* Find the start of the column */
552 for(i = menu->FocusedItem; i != 0 &&
553 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
554 --i); /* empty */
556 if(i == 0)
557 return NO_SELECTED_ITEM;
559 for(--i; i != 0; --i) {
560 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
561 break;
564 TRACE("ret %d.\n", i );
566 return i;
571 /***********************************************************************
572 * MENU_FindItem
574 * Find a menu item. Return a pointer on the item, and modifies *hmenu
575 * in case the item was in a sub-menu.
577 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
579 POPUPMENU *menu;
580 MENUITEM *fallback = NULL;
581 UINT fallback_pos = 0;
582 UINT i;
584 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
585 if (wFlags & MF_BYPOSITION)
587 if (*nPos >= menu->nItems) return NULL;
588 return &menu->items[*nPos];
590 else
592 MENUITEM *item = menu->items;
593 for (i = 0; i < menu->nItems; i++, item++)
595 if (item->fType & MF_POPUP)
597 HMENU hsubmenu = item->hSubMenu;
598 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
599 if (subitem)
601 *hmenu = hsubmenu;
602 return subitem;
604 else if (item->wID == *nPos)
606 /* fallback to this item if nothing else found */
607 fallback_pos = i;
608 fallback = item;
611 else if (item->wID == *nPos)
613 *nPos = i;
614 return item;
619 if (fallback)
620 *nPos = fallback_pos;
622 return fallback;
625 /***********************************************************************
626 * MENU_FindSubMenu
628 * Find a Sub menu. Return the position of the submenu, and modifies
629 * *hmenu in case it is found in another sub-menu.
630 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
632 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
634 POPUPMENU *menu;
635 UINT i;
636 MENUITEM *item;
637 if (((*hmenu)==(HMENU)0xffff) ||
638 (!(menu = MENU_GetMenu(*hmenu))))
639 return NO_SELECTED_ITEM;
640 item = menu->items;
641 for (i = 0; i < menu->nItems; i++, item++) {
642 if(!(item->fType & MF_POPUP)) continue;
643 if (item->hSubMenu == hSubTarget) {
644 return i;
646 else {
647 HMENU hsubmenu = item->hSubMenu;
648 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
649 if (pos != NO_SELECTED_ITEM) {
650 *hmenu = hsubmenu;
651 return pos;
655 return NO_SELECTED_ITEM;
658 /***********************************************************************
659 * MENU_FreeItemData
661 static void MENU_FreeItemData( MENUITEM* item )
663 /* delete text */
664 HeapFree( GetProcessHeap(), 0, item->text );
667 /***********************************************************************
668 * MENU_AdjustMenuItemRect
670 * Adjust menu item rectangle according to scrolling state.
672 static void
673 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
675 if (menu->bScrolling)
677 UINT arrow_bitmap_width, arrow_bitmap_height;
678 BITMAP bmp;
680 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
681 arrow_bitmap_width = bmp.bmWidth;
682 arrow_bitmap_height = bmp.bmHeight;
683 rect->top += arrow_bitmap_height - menu->nScrollPos;
684 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
689 /***********************************************************************
690 * MENU_FindItemByCoords
692 * Find the item at the specified coordinates (screen coords). Does
693 * not work for child windows and therefore should not be called for
694 * an arbitrary system menu.
696 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
697 POINT pt, UINT *pos )
699 MENUITEM *item;
700 UINT i;
701 RECT rect;
703 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
704 pt.x -= rect.left;
705 pt.y -= rect.top;
706 item = menu->items;
707 for (i = 0; i < menu->nItems; i++, item++)
709 rect = item->rect;
710 MENU_AdjustMenuItemRect(menu, &rect);
711 if (PtInRect(&rect, pt))
713 if (pos) *pos = i;
714 return item;
717 return NULL;
721 /***********************************************************************
722 * MENU_FindItemByKey
724 * Find the menu item selected by a key press.
725 * Return item id, -1 if none, -2 if we should close the menu.
727 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
728 WCHAR key, BOOL forceMenuChar )
730 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
732 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
734 if (hmenu)
736 POPUPMENU *menu = MENU_GetMenu( hmenu );
737 MENUITEM *item = menu->items;
738 LRESULT menuchar;
740 if( !forceMenuChar )
742 UINT i;
744 for (i = 0; i < menu->nItems; i++, item++)
746 if( item->text)
748 WCHAR *p = item->text - 2;
751 p = strchrW (p + 2, '&');
753 while (p != NULL && p [1] == '&');
754 if (p && (toupperW(p[1]) == toupperW(key))) return i;
758 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
759 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
760 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
761 if (HIWORD(menuchar) == 1) return (UINT)(-2);
763 return (UINT)(-1);
767 /***********************************************************************
768 * MENU_GetBitmapItemSize
770 * Get the size of a bitmap item.
772 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
773 HWND hwndOwner)
775 BITMAP bm;
776 HBITMAP bmp = lpitem->hbmpItem;
778 size->cx = size->cy = 0;
780 /* check if there is a magic menu item associated with this item */
781 switch( (INT_PTR) bmp )
783 case (INT_PTR)HBMMENU_CALLBACK:
785 MEASUREITEMSTRUCT measItem;
786 measItem.CtlType = ODT_MENU;
787 measItem.CtlID = 0;
788 measItem.itemID = lpitem->wID;
789 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
790 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
791 measItem.itemData = lpitem->dwItemData;
792 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
793 size->cx = measItem.itemWidth;
794 size->cy = measItem.itemHeight;
795 return;
797 break;
798 case (INT_PTR)HBMMENU_SYSTEM:
799 if (lpitem->dwItemData)
801 bmp = (HBITMAP)lpitem->dwItemData;
802 break;
804 /* fall through */
805 case (INT_PTR)HBMMENU_MBAR_RESTORE:
806 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
807 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
808 case (INT_PTR)HBMMENU_MBAR_CLOSE:
809 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
810 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
811 size->cy = size->cx;
812 return;
813 case (INT_PTR)HBMMENU_POPUP_CLOSE:
814 case (INT_PTR)HBMMENU_POPUP_RESTORE:
815 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
816 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
817 FIXME("Magic %p not implemented\n", bmp );
818 return;
820 if (GetObjectW(bmp, sizeof(bm), &bm ))
822 size->cx = bm.bmWidth;
823 size->cy = bm.bmHeight;
827 /***********************************************************************
828 * MENU_DrawBitmapItem
830 * Draw a bitmap item.
832 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
833 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
835 BITMAP bm;
836 DWORD rop;
837 HDC hdcMem;
838 HBITMAP bmp;
839 int w = rect->right - rect->left;
840 int h = rect->bottom - rect->top;
841 int bmp_xoffset = 0;
842 int left, top;
843 HBITMAP hbmToDraw = lpitem->hbmpItem;
844 bmp = hbmToDraw;
846 /* Check if there is a magic menu item associated with this item */
847 if (IS_MAGIC_BITMAP(hbmToDraw))
849 UINT flags = 0;
850 RECT r;
852 switch((INT_PTR)hbmToDraw)
854 case (INT_PTR)HBMMENU_SYSTEM:
855 if (lpitem->dwItemData)
857 bmp = (HBITMAP)lpitem->dwItemData;
858 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
860 else
862 static HBITMAP hBmpSysMenu;
864 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
865 bmp = hBmpSysMenu;
866 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
867 /* only use right half of the bitmap */
868 bmp_xoffset = bm.bmWidth / 2;
869 bm.bmWidth -= bmp_xoffset;
871 goto got_bitmap;
872 case (INT_PTR)HBMMENU_MBAR_RESTORE:
873 flags = DFCS_CAPTIONRESTORE;
874 break;
875 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
876 flags = DFCS_CAPTIONMIN;
877 break;
878 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
879 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
880 break;
881 case (INT_PTR)HBMMENU_MBAR_CLOSE:
882 flags = DFCS_CAPTIONCLOSE;
883 break;
884 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
885 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
886 break;
887 case (INT_PTR)HBMMENU_CALLBACK:
889 DRAWITEMSTRUCT drawItem;
890 drawItem.CtlType = ODT_MENU;
891 drawItem.CtlID = 0;
892 drawItem.itemID = lpitem->wID;
893 drawItem.itemAction = odaction;
894 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
895 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
896 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
897 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
898 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
899 drawItem.hwndItem = (HWND)hmenu;
900 drawItem.hDC = hdc;
901 drawItem.itemData = lpitem->dwItemData;
902 drawItem.rcItem = *rect;
903 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
904 return;
906 break;
907 case (INT_PTR)HBMMENU_POPUP_CLOSE:
908 case (INT_PTR)HBMMENU_POPUP_RESTORE:
909 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
910 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
911 default:
912 FIXME("Magic %p not implemented\n", hbmToDraw);
913 return;
915 r = *rect;
916 InflateRect( &r, -1, -1 );
917 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
918 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
919 return;
922 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
924 got_bitmap:
925 hdcMem = CreateCompatibleDC( hdc );
926 SelectObject( hdcMem, bmp );
928 /* handle fontsize > bitmap_height */
929 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
930 left=rect->left;
931 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
932 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
933 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
934 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
935 DeleteDC( hdcMem );
939 /***********************************************************************
940 * MENU_CalcItemSize
942 * Calculate the size of the menu item and store it in lpitem->rect.
944 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
945 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
947 WCHAR *p;
948 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
949 UINT arrow_bitmap_width;
950 BITMAP bm;
951 INT itemheight;
953 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
954 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
955 (menuBar ? " (MenuBar)" : ""));
957 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
958 arrow_bitmap_width = bm.bmWidth;
960 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
961 if( !menucharsize.cx ) {
962 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
963 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
964 * but it is unlikely an application will depend on that */
965 ODitemheight = HIWORD( GetDialogBaseUnits());
968 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
970 if (lpitem->fType & MF_OWNERDRAW)
972 MEASUREITEMSTRUCT mis;
973 mis.CtlType = ODT_MENU;
974 mis.CtlID = 0;
975 mis.itemID = lpitem->wID;
976 mis.itemData = lpitem->dwItemData;
977 mis.itemHeight = ODitemheight;
978 mis.itemWidth = 0;
979 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
980 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
981 * width of a menufont character to the width of an owner-drawn menu.
983 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
984 if (menuBar) {
985 /* under at least win95 you seem to be given a standard
986 height for the menu and the height value is ignored */
987 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
988 } else
989 lpitem->rect.bottom += mis.itemHeight;
991 TRACE("id=%04lx size=%dx%d\n",
992 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
993 lpitem->rect.bottom-lpitem->rect.top);
994 return;
997 if (lpitem->fType & MF_SEPARATOR)
999 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1000 if( !menuBar)
1001 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1002 return;
1005 itemheight = 0;
1006 lpitem->xTab = 0;
1008 if (!menuBar) {
1009 if (lpitem->hbmpItem) {
1010 SIZE size;
1012 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1013 /* Keep the size of the bitmap in callback mode to be able
1014 * to draw it correctly */
1015 lpitem->bmpsize = size;
1016 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1017 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1018 lpitem->rect.right += size.cx + 2;
1019 itemheight = size.cy + 2;
1021 if( !(lppop->dwStyle & MNS_NOCHECK))
1022 lpitem->rect.right += check_bitmap_width;
1023 lpitem->rect.right += 4 + menucharsize.cx;
1024 lpitem->xTab = lpitem->rect.right;
1025 lpitem->rect.right += arrow_bitmap_width;
1026 } else if (lpitem->hbmpItem) { /* menuBar */
1027 SIZE size;
1029 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1030 lpitem->bmpsize = size;
1031 lpitem->rect.right += size.cx;
1032 if( lpitem->text) lpitem->rect.right += 2;
1033 itemheight = size.cy;
1036 /* it must be a text item - unless it's the system menu */
1037 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1038 HFONT hfontOld = NULL;
1039 RECT rc = lpitem->rect;
1040 LONG txtheight, txtwidth;
1042 if ( lpitem->fState & MFS_DEFAULT ) {
1043 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1045 if (menuBar) {
1046 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1047 DT_SINGLELINE|DT_CALCRECT);
1048 lpitem->rect.right += rc.right - rc.left;
1049 itemheight = max( max( itemheight, txtheight),
1050 GetSystemMetrics( SM_CYMENU) - 1);
1051 lpitem->rect.right += 2 * menucharsize.cx;
1052 } else {
1053 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1054 RECT tmprc = rc;
1055 LONG tmpheight;
1056 int n = (int)( p - lpitem->text);
1057 /* Item contains a tab (only meaningful in popup menus) */
1058 /* get text size before the tab */
1059 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1060 DT_SINGLELINE|DT_CALCRECT);
1061 txtwidth = rc.right - rc.left;
1062 p += 1; /* advance past the Tab */
1063 /* get text size after the tab */
1064 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1065 DT_SINGLELINE|DT_CALCRECT);
1066 lpitem->xTab += txtwidth;
1067 txtheight = max( txtheight, tmpheight);
1068 txtwidth += menucharsize.cx + /* space for the tab */
1069 tmprc.right - tmprc.left; /* space for the short cut */
1070 } else {
1071 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1072 DT_SINGLELINE|DT_CALCRECT);
1073 txtwidth = rc.right - rc.left;
1074 lpitem->xTab += txtwidth;
1076 lpitem->rect.right += 2 + txtwidth;
1077 itemheight = max( itemheight,
1078 max( txtheight + 2, menucharsize.cy + 4));
1080 if (hfontOld) SelectObject (hdc, hfontOld);
1081 } else if( menuBar) {
1082 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1084 lpitem->rect.bottom += itemheight;
1085 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1089 /***********************************************************************
1090 * MENU_GetMaxPopupHeight
1092 static UINT
1093 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1095 if (lppop->cyMax)
1096 return lppop->cyMax;
1097 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1101 /***********************************************************************
1102 * MENU_PopupMenuCalcSize
1104 * Calculate the size of a popup menu.
1106 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1108 MENUITEM *lpitem;
1109 HDC hdc;
1110 int start, i;
1111 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1113 lppop->Width = lppop->Height = 0;
1114 if (lppop->nItems == 0) return;
1115 hdc = GetDC( 0 );
1117 SelectObject( hdc, get_menu_font(FALSE));
1119 start = 0;
1120 maxX = 2 + 1;
1122 lppop->maxBmpSize.cx = 0;
1123 lppop->maxBmpSize.cy = 0;
1125 while (start < lppop->nItems)
1127 lpitem = &lppop->items[start];
1128 orgX = maxX;
1129 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1130 orgX += MENU_COL_SPACE;
1131 orgY = MENU_TOP_MARGIN;
1133 maxTab = maxTabWidth = 0;
1134 /* Parse items until column break or end of menu */
1135 for (i = start; i < lppop->nItems; i++, lpitem++)
1137 if ((i != start) &&
1138 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1140 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1141 maxX = max( maxX, lpitem->rect.right );
1142 orgY = lpitem->rect.bottom;
1143 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1145 maxTab = max( maxTab, lpitem->xTab );
1146 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1150 /* Finish the column (set all items to the largest width found) */
1151 maxX = max( maxX, maxTab + maxTabWidth );
1152 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1154 lpitem->rect.right = maxX;
1155 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1156 lpitem->xTab = maxTab;
1159 lppop->Height = max( lppop->Height, orgY );
1162 lppop->Width = maxX;
1164 /* space for 3d border */
1165 lppop->Height += MENU_BOTTOM_MARGIN;
1166 lppop->Width += 2;
1168 /* Adjust popup height if it exceeds maximum */
1169 maxHeight = MENU_GetMaxPopupHeight(lppop);
1170 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1171 if (lppop->Height >= maxHeight)
1173 lppop->Height = maxHeight;
1174 lppop->bScrolling = TRUE;
1176 else
1178 lppop->bScrolling = FALSE;
1181 ReleaseDC( 0, hdc );
1185 /***********************************************************************
1186 * MENU_MenuBarCalcSize
1188 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1189 * height is off by 1 pixel which causes lengthy window relocations when
1190 * active document window is maximized/restored.
1192 * Calculate the size of the menu bar.
1194 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1195 LPPOPUPMENU lppop, HWND hwndOwner )
1197 MENUITEM *lpitem;
1198 int start, i, orgX, orgY, maxY, helpPos;
1200 if ((lprect == NULL) || (lppop == NULL)) return;
1201 if (lppop->nItems == 0) return;
1202 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1203 lppop->Width = lprect->right - lprect->left;
1204 lppop->Height = 0;
1205 maxY = lprect->top+1;
1206 start = 0;
1207 helpPos = -1;
1208 lppop->maxBmpSize.cx = 0;
1209 lppop->maxBmpSize.cy = 0;
1210 while (start < lppop->nItems)
1212 lpitem = &lppop->items[start];
1213 orgX = lprect->left;
1214 orgY = maxY;
1216 /* Parse items until line break or end of menu */
1217 for (i = start; i < lppop->nItems; i++, lpitem++)
1219 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1220 if ((i != start) &&
1221 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1223 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1224 debug_print_menuitem (" item: ", lpitem, "");
1225 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1227 if (lpitem->rect.right > lprect->right)
1229 if (i != start) break;
1230 else lpitem->rect.right = lprect->right;
1232 maxY = max( maxY, lpitem->rect.bottom );
1233 orgX = lpitem->rect.right;
1236 /* Finish the line (set all items to the largest height found) */
1237 while (start < i) lppop->items[start++].rect.bottom = maxY;
1240 lprect->bottom = maxY;
1241 lppop->Height = lprect->bottom - lprect->top;
1243 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1244 /* the last item (if several lines, only move the last line) */
1245 lpitem = &lppop->items[lppop->nItems-1];
1246 orgY = lpitem->rect.top;
1247 orgX = lprect->right;
1248 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1249 if ( (helpPos==-1) || (helpPos>i) )
1250 break; /* done */
1251 if (lpitem->rect.top != orgY) break; /* Other line */
1252 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1253 lpitem->rect.left += orgX - lpitem->rect.right;
1254 lpitem->rect.right = orgX;
1255 orgX = lpitem->rect.left;
1260 /***********************************************************************
1261 * MENU_DrawScrollArrows
1263 * Draw scroll arrows.
1265 static void
1266 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1268 HDC hdcMem = CreateCompatibleDC(hdc);
1269 HBITMAP hOrigBitmap;
1270 UINT arrow_bitmap_width, arrow_bitmap_height;
1271 BITMAP bmp;
1272 RECT rect;
1274 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1275 arrow_bitmap_width = bmp.bmWidth;
1276 arrow_bitmap_height = bmp.bmHeight;
1279 if (lppop->nScrollPos)
1280 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1281 else
1282 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1283 rect.left = 0;
1284 rect.top = 0;
1285 rect.right = lppop->Width;
1286 rect.bottom = arrow_bitmap_height;
1287 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1288 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1289 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1290 rect.top = lppop->Height - arrow_bitmap_height;
1291 rect.bottom = lppop->Height;
1292 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1293 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1294 SelectObject(hdcMem, get_down_arrow_bitmap());
1295 else
1296 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1297 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1298 lppop->Height - arrow_bitmap_height,
1299 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1300 SelectObject(hdcMem, hOrigBitmap);
1301 DeleteDC(hdcMem);
1305 /***********************************************************************
1306 * draw_popup_arrow
1308 * Draws the popup-menu arrow.
1310 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1311 UINT arrow_bitmap_height)
1313 HDC hdcMem = CreateCompatibleDC( hdc );
1314 HBITMAP hOrigBitmap;
1316 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1317 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1318 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1319 arrow_bitmap_width, arrow_bitmap_height,
1320 hdcMem, 0, 0, SRCCOPY );
1321 SelectObject( hdcMem, hOrigBitmap );
1322 DeleteDC( hdcMem );
1324 /***********************************************************************
1325 * MENU_DrawMenuItem
1327 * Draw a single menu item.
1329 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1330 UINT height, BOOL menuBar, UINT odaction )
1332 RECT rect;
1333 BOOL flat_menu = FALSE;
1334 int bkgnd;
1335 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1336 POPUPMENU *menu = MENU_GetMenu(hmenu);
1337 RECT bmprc;
1339 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1341 if (!menuBar) {
1342 BITMAP bmp;
1343 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1344 arrow_bitmap_width = bmp.bmWidth;
1345 arrow_bitmap_height = bmp.bmHeight;
1348 if (lpitem->fType & MF_SYSMENU)
1350 if( !IsIconic(hwnd) )
1351 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1352 return;
1355 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1356 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1358 /* Setup colors */
1360 if (lpitem->fState & MF_HILITE)
1362 if(menuBar && !flat_menu) {
1363 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1364 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1365 } else {
1366 if(lpitem->fState & MF_GRAYED)
1367 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1368 else
1369 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1370 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1373 else
1375 if (lpitem->fState & MF_GRAYED)
1376 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1377 else
1378 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1379 SetBkColor( hdc, GetSysColor( bkgnd ) );
1382 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1383 rect = lpitem->rect;
1384 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1386 if (lpitem->fType & MF_OWNERDRAW)
1389 ** Experimentation under Windows reveals that an owner-drawn
1390 ** menu is given the rectangle which includes the space it requested
1391 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1392 ** and a popup-menu arrow. This is the value of lpitem->rect.
1393 ** Windows will leave all drawing to the application except for
1394 ** the popup-menu arrow. Windows always draws that itself, after
1395 ** the menu owner has finished drawing.
1397 DRAWITEMSTRUCT dis;
1399 dis.CtlType = ODT_MENU;
1400 dis.CtlID = 0;
1401 dis.itemID = lpitem->wID;
1402 dis.itemData = lpitem->dwItemData;
1403 dis.itemState = 0;
1404 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1405 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1406 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1407 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1408 dis.hwndItem = (HWND)hmenu;
1409 dis.hDC = hdc;
1410 dis.rcItem = rect;
1411 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1412 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1413 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1414 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1415 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1416 /* Draw the popup-menu arrow */
1417 if (lpitem->fType & MF_POPUP)
1418 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1419 arrow_bitmap_height);
1420 return;
1423 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1425 if (lpitem->fState & MF_HILITE)
1427 if (flat_menu)
1429 InflateRect (&rect, -1, -1);
1430 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1431 InflateRect (&rect, 1, 1);
1432 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1434 else
1436 if(menuBar)
1437 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1438 else
1439 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1442 else
1443 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1445 SetBkMode( hdc, TRANSPARENT );
1447 /* vertical separator */
1448 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1450 HPEN oldPen;
1451 RECT rc = rect;
1453 rc.left -= MENU_COL_SPACE / 2 + 1;
1454 rc.top = 3;
1455 rc.bottom = height - 3;
1456 if (flat_menu)
1458 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1459 MoveToEx( hdc, rc.left, rc.top, NULL );
1460 LineTo( hdc, rc.left, rc.bottom );
1461 SelectObject( hdc, oldPen );
1463 else
1464 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1467 /* horizontal separator */
1468 if (lpitem->fType & MF_SEPARATOR)
1470 HPEN oldPen;
1471 RECT rc = rect;
1473 rc.left++;
1474 rc.right--;
1475 rc.top = ( rc.top + rc.bottom) / 2;
1476 if (flat_menu)
1478 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1479 MoveToEx( hdc, rc.left, rc.top, NULL );
1480 LineTo( hdc, rc.right, rc.top );
1481 SelectObject( hdc, oldPen );
1483 else
1484 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1485 return;
1488 /* helper lines for debugging */
1489 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1490 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1491 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1492 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1495 if (lpitem->hbmpItem) {
1496 /* calculate the bitmap rectangle in coordinates relative
1497 * to the item rectangle */
1498 if( menuBar) {
1499 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1500 bmprc.left = 3;
1501 else
1502 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1503 } else {
1504 bmprc.left = 4;
1505 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1506 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1508 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1509 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1510 bmprc.top = 0;
1511 else
1512 bmprc.top = (rect.bottom - rect.top -
1513 lpitem->bmpsize.cy) / 2;
1514 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1517 if (!menuBar)
1519 HBITMAP bm;
1520 INT y = rect.top + rect.bottom;
1521 RECT rc = rect;
1522 int checked = FALSE;
1523 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1524 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1525 /* Draw the check mark
1527 * FIXME:
1528 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1530 if( !(menu->dwStyle & MNS_NOCHECK)) {
1531 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1532 lpitem->hUnCheckBit;
1533 if (bm) /* we have a custom bitmap */
1535 HDC hdcMem = CreateCompatibleDC( hdc );
1537 SelectObject( hdcMem, bm );
1538 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1539 check_bitmap_width, check_bitmap_height,
1540 hdcMem, 0, 0, SRCCOPY );
1541 DeleteDC( hdcMem );
1542 checked = TRUE;
1544 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1546 RECT r;
1547 HBITMAP bm = CreateBitmap( check_bitmap_width,
1548 check_bitmap_height, 1, 1, NULL );
1549 HDC hdcMem = CreateCompatibleDC( hdc );
1551 SelectObject( hdcMem, bm );
1552 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1553 DrawFrameControl( hdcMem, &r, DFC_MENU,
1554 (lpitem->fType & MFT_RADIOCHECK) ?
1555 DFCS_MENUBULLET : DFCS_MENUCHECK );
1556 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1557 hdcMem, 0, 0, SRCCOPY );
1558 DeleteDC( hdcMem );
1559 DeleteObject( bm );
1560 checked = TRUE;
1563 if( lpitem->hbmpItem &&
1564 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1565 POINT origorg;
1566 /* some applications make this assumption on the DC's origin */
1567 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1568 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1569 odaction, FALSE);
1570 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1572 /* Draw the popup-menu arrow */
1573 if (lpitem->fType & MF_POPUP)
1574 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1575 arrow_bitmap_height);
1576 rect.left += 4;
1577 if( !(menu->dwStyle & MNS_NOCHECK))
1578 rect.left += check_bitmap_width;
1579 rect.right -= arrow_bitmap_width;
1581 else if( lpitem->hbmpItem)
1582 { /* Draw the bitmap */
1583 POINT origorg;
1585 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1586 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1587 odaction, menuBar);
1588 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1590 /* process text if present */
1591 if (lpitem->text)
1593 register int i;
1594 HFONT hfontOld = 0;
1596 UINT uFormat = (menuBar) ?
1597 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1598 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1600 if( !(menu->dwStyle & MNS_CHECKORBMP))
1601 rect.left += menu->maxBmpSize.cx;
1603 if ( lpitem->fState & MFS_DEFAULT )
1605 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1608 if (menuBar) {
1609 if( lpitem->hbmpItem)
1610 rect.left += lpitem->bmpsize.cx;
1611 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1612 rect.left += menucharsize.cx;
1613 rect.right -= menucharsize.cx;
1616 for (i = 0; lpitem->text[i]; i++)
1617 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1618 break;
1620 if(lpitem->fState & MF_GRAYED)
1622 if (!(lpitem->fState & MF_HILITE) )
1624 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1625 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1626 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1627 --rect.left; --rect.top; --rect.right; --rect.bottom;
1629 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1632 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1634 /* paint the shortcut text */
1635 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1637 if (lpitem->text[i] == '\t')
1639 rect.left = lpitem->xTab;
1640 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1642 else
1644 rect.right = lpitem->xTab;
1645 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1648 if(lpitem->fState & MF_GRAYED)
1650 if (!(lpitem->fState & MF_HILITE) )
1652 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1653 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1654 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1655 --rect.left; --rect.top; --rect.right; --rect.bottom;
1657 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1659 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1662 if (hfontOld)
1663 SelectObject (hdc, hfontOld);
1668 /***********************************************************************
1669 * MENU_DrawPopupMenu
1671 * Paint a popup menu.
1673 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1675 HBRUSH hPrevBrush = 0;
1676 RECT rect;
1678 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1680 GetClientRect( hwnd, &rect );
1682 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1683 && (SelectObject( hdc, get_menu_font(FALSE))))
1685 HPEN hPrevPen;
1687 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1689 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1690 if( hPrevPen )
1692 POPUPMENU *menu;
1693 BOOL flat_menu = FALSE;
1695 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1696 if (flat_menu)
1697 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1698 else
1699 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1701 if( (menu = MENU_GetMenu( hmenu )))
1703 /* draw menu items */
1704 if( menu->nItems)
1706 MENUITEM *item;
1707 UINT u;
1709 item = menu->items;
1710 for( u = menu->nItems; u > 0; u--, item++)
1711 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1712 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1714 /* draw scroll arrows */
1715 if (menu->bScrolling)
1716 MENU_DrawScrollArrows(menu, hdc);
1718 } else
1720 SelectObject( hdc, hPrevBrush );
1725 /***********************************************************************
1726 * MENU_DrawMenuBar
1728 * Paint a menu bar. Returns the height of the menu bar.
1729 * called from [windows/nonclient.c]
1731 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1732 BOOL suppress_draw)
1734 LPPOPUPMENU lppop;
1735 HFONT hfontOld = 0;
1736 HMENU hMenu = GetMenu(hwnd);
1738 lppop = MENU_GetMenu( hMenu );
1739 if (lppop == NULL || lprect == NULL)
1741 return GetSystemMetrics(SM_CYMENU);
1744 if (suppress_draw)
1746 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1748 if (lppop->Height == 0)
1749 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1751 lprect->bottom = lprect->top + lppop->Height;
1753 if (hfontOld) SelectObject( hDC, hfontOld);
1754 return lppop->Height;
1756 else
1757 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1761 /***********************************************************************
1762 * MENU_ShowPopup
1764 * Display a popup menu.
1766 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1767 INT x, INT y, INT xanchor, INT yanchor )
1769 POPUPMENU *menu;
1770 INT width, height;
1771 POINT pt;
1772 HMONITOR monitor;
1773 MONITORINFO info;
1775 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1776 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1778 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1779 if (menu->FocusedItem != NO_SELECTED_ITEM)
1781 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1782 menu->FocusedItem = NO_SELECTED_ITEM;
1785 /* store the owner for DrawItem */
1786 menu->hwndOwner = hwndOwner;
1788 menu->nScrollPos = 0;
1789 MENU_PopupMenuCalcSize( menu );
1791 /* adjust popup menu pos so that it fits within the desktop */
1793 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1794 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1796 /* FIXME: should use item rect */
1797 pt.x = x;
1798 pt.y = y;
1799 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1800 info.cbSize = sizeof(info);
1801 GetMonitorInfoW( monitor, &info );
1802 if( x + width > info.rcWork.right)
1804 if( xanchor && x >= width - xanchor )
1805 x -= width - xanchor;
1807 if( x + width > info.rcWork.right)
1808 x = info.rcWork.right - width;
1810 if( x < info.rcWork.left ) x = info.rcWork.left;
1812 if( y + height > info.rcWork.bottom)
1814 if( yanchor && y >= height + yanchor )
1815 y -= height + yanchor;
1817 if( y + height > info.rcWork.bottom)
1818 y = info.rcWork.bottom - height;
1820 if( y < info.rcWork.top ) y = info.rcWork.top;
1822 /* NOTE: In Windows, top menu popup is not owned. */
1823 menu->hWnd = CreateWindowExW( WS_EX_TOPMOST, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1824 WS_POPUP, x, y, width, height,
1825 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1826 NULL );
1827 if( !menu->hWnd ) return FALSE;
1828 SetWindowLongPtrW( menu->hWnd, 0, (LONG_PTR)hmenu );
1829 if (!top_popup) top_popup = menu->hWnd;
1831 /* Display the window */
1833 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1834 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1835 UpdateWindow( menu->hWnd );
1836 return TRUE;
1840 /***********************************************************************
1841 * MENU_EnsureMenuItemVisible
1843 static void
1844 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1846 if (lppop->bScrolling)
1848 MENUITEM *item = &lppop->items[wIndex];
1849 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1850 UINT nOldPos = lppop->nScrollPos;
1851 RECT rc;
1852 UINT arrow_bitmap_height;
1853 BITMAP bmp;
1855 GetClientRect(lppop->hWnd, &rc);
1857 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1858 arrow_bitmap_height = bmp.bmHeight;
1860 rc.top += arrow_bitmap_height;
1861 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1863 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1864 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1867 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1868 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1869 MENU_DrawScrollArrows(lppop, hdc);
1871 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1873 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1874 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1875 MENU_DrawScrollArrows(lppop, hdc);
1881 /***********************************************************************
1882 * MENU_SelectItem
1884 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1885 BOOL sendMenuSelect, HMENU topmenu )
1887 LPPOPUPMENU lppop;
1888 HDC hdc;
1890 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1892 lppop = MENU_GetMenu( hmenu );
1893 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1895 if (lppop->FocusedItem == wIndex) return;
1896 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1897 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1898 if (!top_popup) top_popup = lppop->hWnd;
1900 SelectObject( hdc, get_menu_font(FALSE));
1902 /* Clear previous highlighted item */
1903 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1905 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1906 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1907 lppop->Height, !(lppop->wFlags & MF_POPUP),
1908 ODA_SELECT );
1911 /* Highlight new item (if any) */
1912 lppop->FocusedItem = wIndex;
1913 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1915 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1916 lppop->items[wIndex].fState |= MF_HILITE;
1917 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1918 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1919 &lppop->items[wIndex], lppop->Height,
1920 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1922 if (sendMenuSelect)
1924 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1925 SendMessageW( hwndOwner, WM_MENUSELECT,
1926 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1927 ip->fType | ip->fState |
1928 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1931 else if (sendMenuSelect) {
1932 if(topmenu){
1933 int pos;
1934 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1935 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1936 MENUITEM *ip = &ptm->items[pos];
1937 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1938 ip->fType | ip->fState |
1939 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1943 ReleaseDC( lppop->hWnd, hdc );
1947 /***********************************************************************
1948 * MENU_MoveSelection
1950 * Moves currently selected item according to the offset parameter.
1951 * If there is no selection then it should select the last item if
1952 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1954 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1956 INT i;
1957 POPUPMENU *menu;
1959 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1961 menu = MENU_GetMenu( hmenu );
1962 if ((!menu) || (!menu->items)) return;
1964 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1966 if( menu->nItems == 1 ) return; else
1967 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1968 ; i += offset)
1969 if (!(menu->items[i].fType & MF_SEPARATOR))
1971 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1972 return;
1976 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1977 i >= 0 && i < menu->nItems ; i += offset)
1978 if (!(menu->items[i].fType & MF_SEPARATOR))
1980 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1981 return;
1986 /**********************************************************************
1987 * MENU_SetItemData
1989 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1990 * ModifyMenu().
1992 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1993 LPCWSTR str )
1995 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1996 TRACE("flags=%x str=%p\n", flags, str);
1998 if (IS_STRING_ITEM(flags))
2000 LPWSTR prevText = item->text;
2001 if (!str)
2003 flags |= MF_SEPARATOR;
2004 item->text = NULL;
2006 else
2008 LPWSTR text;
2009 /* Item beginning with a backspace is a help item */
2010 if (*str == '\b')
2012 flags |= MF_HELP;
2013 str++;
2015 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2016 return FALSE;
2017 strcpyW( text, str );
2018 item->text = text;
2020 item->hbmpItem = NULL;
2021 HeapFree( GetProcessHeap(), 0, prevText );
2023 else if(( flags & MFT_BITMAP)) {
2024 item->hbmpItem = HBITMAP_32(LOWORD(str));
2025 /* setting bitmap clears text */
2026 HeapFree( GetProcessHeap(), 0, item->text );
2027 item->text = NULL;
2030 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2032 if (flags & MF_OWNERDRAW)
2033 item->dwItemData = (DWORD_PTR)str;
2034 else
2035 item->dwItemData = 0;
2037 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2038 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2040 if (flags & MF_POPUP)
2042 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2043 if (menu) menu->wFlags |= MF_POPUP;
2044 else
2046 item->wID = 0;
2047 item->hSubMenu = 0;
2048 item->fType = 0;
2049 item->fState = 0;
2050 return FALSE;
2054 item->wID = id;
2055 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2057 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2058 flags |= MF_POPUP; /* keep popup */
2060 item->fType = flags & TYPE_MASK;
2061 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2062 for ModifyMenu, but Windows accepts it */
2063 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2065 /* Don't call SetRectEmpty here! */
2067 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2068 return TRUE;
2072 /**********************************************************************
2073 * MENU_InsertItem
2075 * Insert (allocate) a new item into a menu.
2077 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2079 MENUITEM *newItems;
2080 POPUPMENU *menu;
2082 if (!(menu = MENU_GetMenu(hMenu)))
2083 return NULL;
2085 /* Find where to insert new item */
2087 if (flags & MF_BYPOSITION) {
2088 if (pos > menu->nItems)
2089 pos = menu->nItems;
2090 } else {
2091 if (!MENU_FindItem( &hMenu, &pos, flags ))
2092 pos = menu->nItems;
2093 else {
2094 if (!(menu = MENU_GetMenu( hMenu )))
2095 return NULL;
2099 /* Make sure that MDI system buttons stay on the right side.
2100 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2101 * regardless of their id.
2103 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2104 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2105 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2106 pos--;
2108 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2110 /* Create new items array */
2112 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2113 if (!newItems)
2115 WARN("allocation failed\n" );
2116 return NULL;
2118 if (menu->nItems > 0)
2120 /* Copy the old array into the new one */
2121 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2122 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2123 (menu->nItems-pos)*sizeof(MENUITEM) );
2124 HeapFree( GetProcessHeap(), 0, menu->items );
2126 menu->items = newItems;
2127 menu->nItems++;
2128 memset( &newItems[pos], 0, sizeof(*newItems) );
2129 menu->Height = 0; /* force size recalculate */
2130 return &newItems[pos];
2134 /**********************************************************************
2135 * MENU_ParseResource
2137 * Parse a standard menu resource and add items to the menu.
2138 * Return a pointer to the end of the resource.
2140 * NOTE: flags is equivalent to the mtOption field
2142 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2144 WORD flags, id = 0;
2145 LPCSTR str;
2146 BOOL end_flag;
2150 flags = GET_WORD(res);
2151 end_flag = flags & MF_END;
2152 /* Remove MF_END because it has the same value as MF_HILITE */
2153 flags &= ~MF_END;
2154 res += sizeof(WORD);
2155 if (!(flags & MF_POPUP))
2157 id = GET_WORD(res);
2158 res += sizeof(WORD);
2160 str = res;
2161 if (!unicode) res += strlen(str) + 1;
2162 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2163 if (flags & MF_POPUP)
2165 HMENU hSubMenu = CreatePopupMenu();
2166 if (!hSubMenu) return NULL;
2167 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2168 return NULL;
2169 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2170 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2172 else /* Not a popup */
2174 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2175 else AppendMenuW( hMenu, flags, id,
2176 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2178 } while (!end_flag);
2179 return res;
2183 /**********************************************************************
2184 * MENUEX_ParseResource
2186 * Parse an extended menu resource and add items to the menu.
2187 * Return a pointer to the end of the resource.
2189 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2191 WORD resinfo;
2192 do {
2193 MENUITEMINFOW mii;
2195 mii.cbSize = sizeof(mii);
2196 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2197 mii.fType = GET_DWORD(res);
2198 res += sizeof(DWORD);
2199 mii.fState = GET_DWORD(res);
2200 res += sizeof(DWORD);
2201 mii.wID = GET_DWORD(res);
2202 res += sizeof(DWORD);
2203 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2204 res += sizeof(WORD);
2205 /* Align the text on a word boundary. */
2206 res += (~((UINT_PTR)res - 1)) & 1;
2207 mii.dwTypeData = (LPWSTR) res;
2208 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2209 /* Align the following fields on a dword boundary. */
2210 res += (~((UINT_PTR)res - 1)) & 3;
2212 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2213 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2215 if (resinfo & 1) { /* Pop-up? */
2216 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2217 res += sizeof(DWORD);
2218 mii.hSubMenu = CreatePopupMenu();
2219 if (!mii.hSubMenu)
2220 return NULL;
2221 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2222 DestroyMenu(mii.hSubMenu);
2223 return NULL;
2225 mii.fMask |= MIIM_SUBMENU;
2226 mii.fType |= MF_POPUP;
2228 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2230 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2231 mii.wID, mii.fType);
2232 mii.fType |= MF_SEPARATOR;
2234 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2235 } while (!(resinfo & MF_END));
2236 return res;
2240 /***********************************************************************
2241 * MENU_GetSubPopup
2243 * Return the handle of the selected sub-popup menu (if any).
2245 static HMENU MENU_GetSubPopup( HMENU hmenu )
2247 POPUPMENU *menu;
2248 MENUITEM *item;
2250 menu = MENU_GetMenu( hmenu );
2252 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2254 item = &menu->items[menu->FocusedItem];
2255 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2256 return item->hSubMenu;
2257 return 0;
2261 /***********************************************************************
2262 * MENU_HideSubPopups
2264 * Hide the sub-popup menus of this menu.
2266 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2267 BOOL sendMenuSelect )
2269 POPUPMENU *menu = MENU_GetMenu( hmenu );
2271 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2273 if (menu && top_popup)
2275 HMENU hsubmenu;
2276 POPUPMENU *submenu;
2277 MENUITEM *item;
2279 if (menu->FocusedItem != NO_SELECTED_ITEM)
2281 item = &menu->items[menu->FocusedItem];
2282 if (!(item->fType & MF_POPUP) ||
2283 !(item->fState & MF_MOUSESELECT)) return;
2284 item->fState &= ~MF_MOUSESELECT;
2285 hsubmenu = item->hSubMenu;
2286 } else return;
2288 submenu = MENU_GetMenu( hsubmenu );
2289 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2290 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2291 DestroyWindow( submenu->hWnd );
2292 submenu->hWnd = 0;
2297 /***********************************************************************
2298 * MENU_ShowSubPopup
2300 * Display the sub-menu of the selected item of this menu.
2301 * Return the handle of the submenu, or hmenu if no submenu to display.
2303 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2304 BOOL selectFirst, UINT wFlags )
2306 RECT rect;
2307 POPUPMENU *menu;
2308 MENUITEM *item;
2309 HDC hdc;
2311 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2313 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2315 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2317 item = &menu->items[menu->FocusedItem];
2318 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2319 return hmenu;
2321 /* message must be sent before using item,
2322 because nearly everything may be changed by the application ! */
2324 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2325 if (!(wFlags & TPM_NONOTIFY))
2326 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2327 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2329 item = &menu->items[menu->FocusedItem];
2330 rect = item->rect;
2332 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2333 if (!(item->fState & MF_HILITE))
2335 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2336 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2338 SelectObject( hdc, get_menu_font(FALSE));
2340 item->fState |= MF_HILITE;
2341 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2342 ReleaseDC( menu->hWnd, hdc );
2344 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2345 item->rect = rect;
2347 item->fState |= MF_MOUSESELECT;
2349 if (IS_SYSTEM_MENU(menu))
2351 MENU_InitSysMenuPopup(item->hSubMenu,
2352 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2353 GetClassLongW( menu->hWnd, GCL_STYLE));
2355 NC_GetSysPopupPos( menu->hWnd, &rect );
2356 rect.top = rect.bottom;
2357 rect.right = GetSystemMetrics(SM_CXSIZE);
2358 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2360 else
2362 GetWindowRect( menu->hWnd, &rect );
2363 if (menu->wFlags & MF_POPUP)
2365 RECT rc = item->rect;
2367 MENU_AdjustMenuItemRect(menu, &rc);
2369 /* The first item in the popup menu has to be at the
2370 same y position as the focused menu item */
2371 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2372 rect.top += rc.top - MENU_TOP_MARGIN;
2373 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2374 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2375 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2377 else
2379 rect.left += item->rect.left;
2380 rect.top += item->rect.bottom;
2381 rect.right = item->rect.right - item->rect.left;
2382 rect.bottom = item->rect.bottom - item->rect.top;
2386 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2387 rect.left, rect.top, rect.right, rect.bottom );
2388 if (selectFirst)
2389 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2390 return item->hSubMenu;
2395 /**********************************************************************
2396 * MENU_IsMenuActive
2398 HWND MENU_IsMenuActive(void)
2400 return top_popup;
2403 /***********************************************************************
2404 * MENU_PtMenu
2406 * Walks menu chain trying to find a menu pt maps to.
2408 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2410 POPUPMENU *menu = MENU_GetMenu( hMenu );
2411 UINT item = menu->FocusedItem;
2412 HMENU ret;
2414 /* try subpopup first (if any) */
2415 ret = (item != NO_SELECTED_ITEM &&
2416 (menu->items[item].fType & MF_POPUP) &&
2417 (menu->items[item].fState & MF_MOUSESELECT))
2418 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2420 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2422 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2423 if( menu->wFlags & MF_POPUP )
2425 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2427 else if (ht == HTSYSMENU)
2428 ret = get_win_sys_menu( menu->hWnd );
2429 else if (ht == HTMENU)
2430 ret = GetMenu( menu->hWnd );
2432 return ret;
2435 /***********************************************************************
2436 * MENU_ExecFocusedItem
2438 * Execute a menu item (for instance when user pressed Enter).
2439 * Return the wID of the executed item. Otherwise, -1 indicating
2440 * that no menu item was executed, -2 if a popup is shown;
2441 * Have to receive the flags for the TrackPopupMenu options to avoid
2442 * sending unwanted message.
2445 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2447 MENUITEM *item;
2448 POPUPMENU *menu = MENU_GetMenu( hMenu );
2450 TRACE("%p hmenu=%p\n", pmt, hMenu);
2452 if (!menu || !menu->nItems ||
2453 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2455 item = &menu->items[menu->FocusedItem];
2457 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2459 if (!(item->fType & MF_POPUP))
2461 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2463 /* If TPM_RETURNCMD is set you return the id, but
2464 do not send a message to the owner */
2465 if(!(wFlags & TPM_RETURNCMD))
2467 if( menu->wFlags & MF_SYSMENU )
2468 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2469 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2470 else
2472 if (menu->dwStyle & MNS_NOTIFYBYPOS)
2473 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2474 (LPARAM)hMenu);
2475 else
2476 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2479 return item->wID;
2482 else
2484 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2485 return -2;
2488 return -1;
2491 /***********************************************************************
2492 * MENU_SwitchTracking
2494 * Helper function for menu navigation routines.
2496 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2498 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2499 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2501 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2503 if( pmt->hTopMenu != hPtMenu &&
2504 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2506 /* both are top level menus (system and menu-bar) */
2507 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2508 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2509 pmt->hTopMenu = hPtMenu;
2511 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2512 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2516 /***********************************************************************
2517 * MENU_ButtonDown
2519 * Return TRUE if we can go on with menu tracking.
2521 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2523 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2525 if (hPtMenu)
2527 UINT id = 0;
2528 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2529 MENUITEM *item;
2531 if( IS_SYSTEM_MENU(ptmenu) )
2532 item = ptmenu->items;
2533 else
2534 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2536 if( item )
2538 if( ptmenu->FocusedItem != id )
2539 MENU_SwitchTracking( pmt, hPtMenu, id );
2541 /* If the popup menu is not already "popped" */
2542 if(!(item->fState & MF_MOUSESELECT ))
2544 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2547 return TRUE;
2549 /* Else the click was on the menu bar, finish the tracking */
2551 return FALSE;
2554 /***********************************************************************
2555 * MENU_ButtonUp
2557 * Return the value of MENU_ExecFocusedItem if
2558 * the selected item was not a popup. Else open the popup.
2559 * A -1 return value indicates that we go on with menu tracking.
2562 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2564 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2566 if (hPtMenu)
2568 UINT id = 0;
2569 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2570 MENUITEM *item;
2572 if( IS_SYSTEM_MENU(ptmenu) )
2573 item = ptmenu->items;
2574 else
2575 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2577 if( item && (ptmenu->FocusedItem == id ))
2579 debug_print_menuitem ("FocusedItem: ", item, "");
2581 if( !(item->fType & MF_POPUP) )
2583 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2584 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2585 return executedMenuId;
2588 /* If we are dealing with the top-level menu */
2589 /* and this is a click on an already "popped" item: */
2590 /* Stop the menu tracking and close the opened submenus */
2591 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2592 return 0;
2594 ptmenu->bTimeToHide = TRUE;
2596 return -1;
2600 /***********************************************************************
2601 * MENU_MouseMove
2603 * Return TRUE if we can go on with menu tracking.
2605 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2607 UINT id = NO_SELECTED_ITEM;
2608 POPUPMENU *ptmenu = NULL;
2610 if( hPtMenu )
2612 ptmenu = MENU_GetMenu( hPtMenu );
2613 if( IS_SYSTEM_MENU(ptmenu) )
2614 id = 0;
2615 else
2616 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2619 if( id == NO_SELECTED_ITEM )
2621 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2622 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2625 else if( ptmenu->FocusedItem != id )
2627 MENU_SwitchTracking( pmt, hPtMenu, id );
2628 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2630 return TRUE;
2634 /***********************************************************************
2635 * MENU_SetCapture
2637 static void MENU_SetCapture( HWND hwnd )
2639 HWND previous = 0;
2641 SERVER_START_REQ( set_capture_window )
2643 req->handle = hwnd;
2644 req->flags = CAPTURE_MENU;
2645 if (!wine_server_call_err( req ))
2647 previous = reply->previous;
2648 hwnd = reply->full_handle;
2651 SERVER_END_REQ;
2653 if (previous && previous != hwnd)
2654 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2658 /***********************************************************************
2659 * MENU_DoNextMenu
2661 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2663 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2665 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2666 BOOL atEnd = FALSE;
2668 /* When skipping left, we need to do something special after the
2669 first menu. */
2670 if (vk == VK_LEFT && menu->FocusedItem == 0)
2672 atEnd = TRUE;
2674 /* When skipping right, for the non-system menu, we need to
2675 handle the last non-special menu item (ie skip any window
2676 icons such as MDI maximize, restore or close) */
2677 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2679 int i = menu->FocusedItem + 1;
2680 while (i < menu->nItems) {
2681 if ((menu->items[i].wID >= SC_SIZE &&
2682 menu->items[i].wID <= SC_RESTORE)) {
2683 i++;
2684 } else break;
2686 if (i == menu->nItems) {
2687 atEnd = TRUE;
2690 /* When skipping right, we need to cater for the system menu */
2691 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2693 if (menu->FocusedItem == (menu->nItems - 1)) {
2694 atEnd = TRUE;
2698 if( atEnd )
2700 MDINEXTMENU next_menu;
2701 HMENU hNewMenu;
2702 HWND hNewWnd;
2703 UINT id = 0;
2705 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2706 next_menu.hmenuNext = 0;
2707 next_menu.hwndNext = 0;
2708 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2710 TRACE("%p [%p] -> %p [%p]\n",
2711 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2713 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2715 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2716 hNewWnd = pmt->hOwnerWnd;
2717 if( IS_SYSTEM_MENU(menu) )
2719 /* switch to the menu bar */
2721 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2723 if( vk == VK_LEFT )
2725 menu = MENU_GetMenu( hNewMenu );
2726 id = menu->nItems - 1;
2728 /* Skip backwards over any system predefined icons,
2729 eg. MDI close, restore etc icons */
2730 while ((id > 0) &&
2731 (menu->items[id].wID >= SC_SIZE &&
2732 menu->items[id].wID <= SC_RESTORE)) id--;
2735 else if (style & WS_SYSMENU )
2737 /* switch to the system menu */
2738 hNewMenu = get_win_sys_menu( hNewWnd );
2740 else return FALSE;
2742 else /* application returned a new menu to switch to */
2744 hNewMenu = next_menu.hmenuNext;
2745 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2747 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2749 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2751 if (style & WS_SYSMENU &&
2752 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2754 /* get the real system menu */
2755 hNewMenu = get_win_sys_menu(hNewWnd);
2757 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2759 /* FIXME: Not sure what to do here;
2760 * perhaps try to track hNewMenu as a popup? */
2762 TRACE(" -- got confused.\n");
2763 return FALSE;
2766 else return FALSE;
2769 if( hNewMenu != pmt->hTopMenu )
2771 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2772 FALSE, 0 );
2773 if( pmt->hCurrentMenu != pmt->hTopMenu )
2774 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2777 if( hNewWnd != pmt->hOwnerWnd )
2779 pmt->hOwnerWnd = hNewWnd;
2780 MENU_SetCapture( pmt->hOwnerWnd );
2783 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2784 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2786 return TRUE;
2788 return FALSE;
2791 /***********************************************************************
2792 * MENU_SuspendPopup
2794 * The idea is not to show the popup if the next input message is
2795 * going to hide it anyway.
2797 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2799 MSG msg;
2801 msg.hwnd = pmt->hOwnerWnd;
2803 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2804 pmt->trackFlags |= TF_SKIPREMOVE;
2806 switch( uMsg )
2808 case WM_KEYDOWN:
2809 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2810 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2812 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2813 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2814 if( msg.message == WM_KEYDOWN &&
2815 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2817 pmt->trackFlags |= TF_SUSPENDPOPUP;
2818 return TRUE;
2821 break;
2824 /* failures go through this */
2825 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2826 return FALSE;
2829 /***********************************************************************
2830 * MENU_KeyEscape
2832 * Handle a VK_ESCAPE key event in a menu.
2834 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2836 BOOL bEndMenu = TRUE;
2838 if (pmt->hCurrentMenu != pmt->hTopMenu)
2840 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2842 if (menu->wFlags & MF_POPUP)
2844 HMENU hmenutmp, hmenuprev;
2846 hmenuprev = hmenutmp = pmt->hTopMenu;
2848 /* close topmost popup */
2849 while (hmenutmp != pmt->hCurrentMenu)
2851 hmenuprev = hmenutmp;
2852 hmenutmp = MENU_GetSubPopup( hmenuprev );
2855 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2856 pmt->hCurrentMenu = hmenuprev;
2857 bEndMenu = FALSE;
2861 return bEndMenu;
2864 /***********************************************************************
2865 * MENU_KeyLeft
2867 * Handle a VK_LEFT key event in a menu.
2869 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2871 POPUPMENU *menu;
2872 HMENU hmenutmp, hmenuprev;
2873 UINT prevcol;
2875 hmenuprev = hmenutmp = pmt->hTopMenu;
2876 menu = MENU_GetMenu( hmenutmp );
2878 /* Try to move 1 column left (if possible) */
2879 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2880 NO_SELECTED_ITEM ) {
2882 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2883 prevcol, TRUE, 0 );
2884 return;
2887 /* close topmost popup */
2888 while (hmenutmp != pmt->hCurrentMenu)
2890 hmenuprev = hmenutmp;
2891 hmenutmp = MENU_GetSubPopup( hmenuprev );
2894 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2895 pmt->hCurrentMenu = hmenuprev;
2897 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2899 /* move menu bar selection if no more popups are left */
2901 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2902 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2904 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2906 /* A sublevel menu was displayed - display the next one
2907 * unless there is another displacement coming up */
2909 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2910 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2911 pmt->hTopMenu, TRUE, wFlags);
2917 /***********************************************************************
2918 * MENU_KeyRight
2920 * Handle a VK_RIGHT key event in a menu.
2922 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2924 HMENU hmenutmp;
2925 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2926 UINT nextcol;
2928 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2929 pmt->hCurrentMenu,
2930 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2931 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2933 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2935 /* If already displaying a popup, try to display sub-popup */
2937 hmenutmp = pmt->hCurrentMenu;
2938 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2940 /* if subpopup was displayed then we are done */
2941 if (hmenutmp != pmt->hCurrentMenu) return;
2944 /* Check to see if there's another column */
2945 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2946 NO_SELECTED_ITEM ) {
2947 TRACE("Going to %d.\n", nextcol );
2948 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2949 nextcol, TRUE, 0 );
2950 return;
2953 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2955 if( pmt->hCurrentMenu != pmt->hTopMenu )
2957 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2958 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2959 } else hmenutmp = 0;
2961 /* try to move to the next item */
2962 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2963 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2965 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2966 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2967 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2968 pmt->hTopMenu, TRUE, wFlags);
2972 /***********************************************************************
2973 * MENU_TrackMenu
2975 * Menu tracking code.
2977 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2978 HWND hwnd, const RECT *lprect )
2980 MSG msg;
2981 POPUPMENU *menu;
2982 BOOL fRemove;
2983 INT executedMenuId = -1;
2984 MTRACKER mt;
2985 BOOL enterIdleSent = FALSE;
2987 mt.trackFlags = 0;
2988 mt.hCurrentMenu = hmenu;
2989 mt.hTopMenu = hmenu;
2990 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2991 mt.pt.x = x;
2992 mt.pt.y = y;
2994 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2995 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2997 fEndMenu = FALSE;
2998 if (!(menu = MENU_GetMenu( hmenu )))
3000 WARN("Invalid menu handle %p\n", hmenu);
3001 SetLastError(ERROR_INVALID_MENU_HANDLE);
3002 return FALSE;
3005 if (wFlags & TPM_BUTTONDOWN)
3007 /* Get the result in order to start the tracking or not */
3008 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3009 fEndMenu = !fRemove;
3012 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3014 MENU_SetCapture( mt.hOwnerWnd );
3016 while (!fEndMenu)
3018 menu = MENU_GetMenu( mt.hCurrentMenu );
3019 if (!menu) /* sometimes happens if I do a window manager close */
3020 break;
3022 /* we have to keep the message in the queue until it's
3023 * clear that menu loop is not over yet. */
3025 for (;;)
3027 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3029 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3030 /* remove the message from the queue */
3031 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3033 else
3035 if (!enterIdleSent)
3037 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3038 enterIdleSent = TRUE;
3039 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3041 WaitMessage();
3045 /* check if EndMenu() tried to cancel us, by posting this message */
3046 if(msg.message == WM_CANCELMODE)
3048 /* we are now out of the loop */
3049 fEndMenu = TRUE;
3051 /* remove the message from the queue */
3052 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3054 /* break out of internal loop, ala ESCAPE */
3055 break;
3058 TranslateMessage( &msg );
3059 mt.pt = msg.pt;
3061 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3062 enterIdleSent=FALSE;
3064 fRemove = FALSE;
3065 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3068 * Use the mouse coordinates in lParam instead of those in the MSG
3069 * struct to properly handle synthetic messages. They are already
3070 * in screen coordinates.
3072 mt.pt.x = (short)LOWORD(msg.lParam);
3073 mt.pt.y = (short)HIWORD(msg.lParam);
3075 /* Find a menu for this mouse event */
3076 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3078 switch(msg.message)
3080 /* no WM_NC... messages in captured state */
3082 case WM_RBUTTONDBLCLK:
3083 case WM_RBUTTONDOWN:
3084 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3085 /* fall through */
3086 case WM_LBUTTONDBLCLK:
3087 case WM_LBUTTONDOWN:
3088 /* If the message belongs to the menu, removes it from the queue */
3089 /* Else, end menu tracking */
3090 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3091 fEndMenu = !fRemove;
3092 break;
3094 case WM_RBUTTONUP:
3095 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3096 /* fall through */
3097 case WM_LBUTTONUP:
3098 /* Check if a menu was selected by the mouse */
3099 if (hmenu)
3101 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3102 TRACE("executedMenuId %d\n", executedMenuId);
3104 /* End the loop if executedMenuId is an item ID */
3105 /* or if the job was done (executedMenuId = 0). */
3106 fEndMenu = fRemove = (executedMenuId != -1);
3108 /* No menu was selected by the mouse */
3109 /* if the function was called by TrackPopupMenu, continue
3110 with the menu tracking. If not, stop it */
3111 else
3112 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3114 break;
3116 case WM_MOUSEMOVE:
3117 /* the selected menu item must be changed every time */
3118 /* the mouse moves. */
3120 if (hmenu)
3121 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3123 } /* switch(msg.message) - mouse */
3125 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3127 fRemove = TRUE; /* Keyboard messages are always removed */
3128 switch(msg.message)
3130 case WM_KEYDOWN:
3131 case WM_SYSKEYDOWN:
3132 switch(msg.wParam)
3134 case VK_MENU:
3135 fEndMenu = TRUE;
3136 break;
3138 case VK_HOME:
3139 case VK_END:
3140 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3141 NO_SELECTED_ITEM, FALSE, 0 );
3142 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3143 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3144 break;
3146 case VK_UP:
3147 case VK_DOWN: /* If on menu bar, pull-down the menu */
3149 menu = MENU_GetMenu( mt.hCurrentMenu );
3150 if (!(menu->wFlags & MF_POPUP))
3151 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3152 else /* otherwise try to move selection */
3153 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3154 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3155 break;
3157 case VK_LEFT:
3158 MENU_KeyLeft( &mt, wFlags );
3159 break;
3161 case VK_RIGHT:
3162 MENU_KeyRight( &mt, wFlags );
3163 break;
3165 case VK_ESCAPE:
3166 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3167 break;
3169 case VK_F1:
3171 HELPINFO hi;
3172 hi.cbSize = sizeof(HELPINFO);
3173 hi.iContextType = HELPINFO_MENUITEM;
3174 if (menu->FocusedItem == NO_SELECTED_ITEM)
3175 hi.iCtrlId = 0;
3176 else
3177 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3178 hi.hItemHandle = hmenu;
3179 hi.dwContextId = menu->dwContextHelpID;
3180 hi.MousePos = msg.pt;
3181 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3182 break;
3185 default:
3186 break;
3188 break; /* WM_KEYDOWN */
3190 case WM_CHAR:
3191 case WM_SYSCHAR:
3193 UINT pos;
3195 if (msg.wParam == '\r' || msg.wParam == ' ')
3197 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3198 fEndMenu = (executedMenuId != -2);
3200 break;
3203 /* Hack to avoid control chars. */
3204 /* We will find a better way real soon... */
3205 if (msg.wParam < 32) break;
3207 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3208 LOWORD(msg.wParam), FALSE );
3209 if (pos == (UINT)-2) fEndMenu = TRUE;
3210 else if (pos == (UINT)-1) MessageBeep(0);
3211 else
3213 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3214 TRUE, 0 );
3215 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3216 fEndMenu = (executedMenuId != -2);
3219 break;
3220 } /* switch(msg.message) - kbd */
3222 else
3224 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3225 DispatchMessageW( &msg );
3226 continue;
3229 if (!fEndMenu) fRemove = TRUE;
3231 /* finally remove message from the queue */
3233 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3234 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3235 else mt.trackFlags &= ~TF_SKIPREMOVE;
3238 MENU_SetCapture(0); /* release the capture */
3240 /* If dropdown is still painted and the close box is clicked on
3241 then the menu will be destroyed as part of the DispatchMessage above.
3242 This will then invalidate the menu handle in mt.hTopMenu. We should
3243 check for this first. */
3244 if( IsMenu( mt.hTopMenu ) )
3246 menu = MENU_GetMenu( mt.hTopMenu );
3248 if( IsWindow( mt.hOwnerWnd ) )
3250 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3252 if (menu && (menu->wFlags & MF_POPUP))
3254 DestroyWindow( menu->hWnd );
3255 menu->hWnd = 0;
3257 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3258 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3261 /* Reset the variable for hiding menu */
3262 if( menu ) menu->bTimeToHide = FALSE;
3265 /* The return value is only used by TrackPopupMenu */
3266 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3267 if (executedMenuId == -1) executedMenuId = 0;
3268 return executedMenuId;
3271 /***********************************************************************
3272 * MENU_InitTracking
3274 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3276 POPUPMENU *menu;
3278 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3280 HideCaret(0);
3282 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3283 if (!(wFlags & TPM_NONOTIFY))
3284 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3286 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3288 if (!(wFlags & TPM_NONOTIFY))
3290 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3291 /* If an app changed/recreated menu bar entries in WM_INITMENU
3292 * menu sizes will be recalculated once the menu created/shown.
3296 /* This makes the menus of applications built with Delphi work.
3297 * It also enables menus to be displayed in more than one window,
3298 * but there are some bugs left that need to be fixed in this case.
3300 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3302 return TRUE;
3304 /***********************************************************************
3305 * MENU_ExitTracking
3307 static BOOL MENU_ExitTracking(HWND hWnd)
3309 TRACE("hwnd=%p\n", hWnd);
3311 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3312 ShowCaret(0);
3313 top_popup = 0;
3314 return TRUE;
3317 /***********************************************************************
3318 * MENU_TrackMouseMenuBar
3320 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3322 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3324 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3325 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3327 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3329 if (IsMenu(hMenu))
3331 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3332 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3333 MENU_ExitTracking(hWnd);
3338 /***********************************************************************
3339 * MENU_TrackKbdMenuBar
3341 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3343 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3345 UINT uItem = NO_SELECTED_ITEM;
3346 HMENU hTrackMenu;
3347 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3349 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3351 /* find window that has a menu */
3353 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3354 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3356 /* check if we have to track a system menu */
3358 hTrackMenu = GetMenu( hwnd );
3359 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3361 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3362 hTrackMenu = get_win_sys_menu( hwnd );
3363 uItem = 0;
3364 wParam |= HTSYSMENU; /* prevent item lookup */
3367 if (!IsMenu( hTrackMenu )) return;
3369 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3371 if( wChar && wChar != ' ' )
3373 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3374 if ( uItem >= (UINT)(-2) )
3376 if( uItem == (UINT)(-1) ) MessageBeep(0);
3377 /* schedule end of menu tracking */
3378 wFlags |= TF_ENDMENU;
3379 goto track_menu;
3383 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3385 if (wParam & HTSYSMENU && wChar != ' ')
3387 /* prevent sysmenu activation for managed windows on Alt down/up */
3388 if (GetPropA( hwnd, "__wine_x11_managed" ))
3389 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3391 else
3393 if( uItem == NO_SELECTED_ITEM )
3394 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3395 else
3396 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3399 track_menu:
3400 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3401 MENU_ExitTracking( hwnd );
3405 /**********************************************************************
3406 * TrackPopupMenu (USER32.@)
3408 * Like the win32 API, the function return the command ID only if the
3409 * flag TPM_RETURNCMD is on.
3412 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3413 INT nReserved, HWND hWnd, const RECT *lpRect )
3415 BOOL ret = FALSE;
3417 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3418 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3420 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3422 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3423 if (!(wFlags & TPM_NONOTIFY))
3424 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3426 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3427 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3428 MENU_ExitTracking(hWnd);
3430 return ret;
3433 /**********************************************************************
3434 * TrackPopupMenuEx (USER32.@)
3436 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3437 HWND hWnd, LPTPMPARAMS lpTpm )
3439 FIXME("not fully implemented\n" );
3440 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3441 lpTpm ? &lpTpm->rcExclude : NULL );
3444 /***********************************************************************
3445 * PopupMenuWndProc
3447 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3449 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3451 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3453 switch(message)
3455 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3456 return MA_NOACTIVATE;
3458 case WM_PAINT:
3460 PAINTSTRUCT ps;
3461 BeginPaint( hwnd, &ps );
3462 MENU_DrawPopupMenu( hwnd, ps.hdc,
3463 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3464 EndPaint( hwnd, &ps );
3465 return 0;
3467 case WM_ERASEBKGND:
3468 return 1;
3470 case WM_DESTROY:
3471 /* zero out global pointer in case resident popup window was destroyed. */
3472 if (hwnd == top_popup) top_popup = 0;
3473 break;
3475 case WM_SHOWWINDOW:
3477 if( wParam )
3479 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3481 else
3482 SetWindowLongPtrW( hwnd, 0, 0 );
3483 break;
3485 default:
3486 return DefWindowProcW( hwnd, message, wParam, lParam );
3488 return 0;
3492 /***********************************************************************
3493 * MENU_GetMenuBarHeight
3495 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3497 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3498 INT orgX, INT orgY )
3500 HDC hdc;
3501 RECT rectBar;
3502 LPPOPUPMENU lppop;
3504 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3506 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3508 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3509 SelectObject( hdc, get_menu_font(FALSE));
3510 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3511 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3512 ReleaseDC( hwnd, hdc );
3513 return lppop->Height;
3517 /*******************************************************************
3518 * ChangeMenuA (USER32.@)
3520 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3521 UINT id, UINT flags )
3523 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3524 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3525 id, data );
3526 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3527 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3528 id, data );
3529 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3530 flags & MF_BYPOSITION ? pos : id,
3531 flags & ~MF_REMOVE );
3532 /* Default: MF_INSERT */
3533 return InsertMenuA( hMenu, pos, flags, id, data );
3537 /*******************************************************************
3538 * ChangeMenuW (USER32.@)
3540 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3541 UINT id, UINT flags )
3543 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3544 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3545 id, data );
3546 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3547 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3548 id, data );
3549 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3550 flags & MF_BYPOSITION ? pos : id,
3551 flags & ~MF_REMOVE );
3552 /* Default: MF_INSERT */
3553 return InsertMenuW( hMenu, pos, flags, id, data );
3557 /*******************************************************************
3558 * CheckMenuItem (USER32.@)
3560 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3562 MENUITEM *item;
3563 DWORD ret;
3565 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3566 ret = item->fState & MF_CHECKED;
3567 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3568 else item->fState &= ~MF_CHECKED;
3569 return ret;
3573 /**********************************************************************
3574 * EnableMenuItem (USER32.@)
3576 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3578 UINT oldflags;
3579 MENUITEM *item;
3580 POPUPMENU *menu;
3582 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3584 /* Get the Popupmenu to access the owner menu */
3585 if (!(menu = MENU_GetMenu(hMenu)))
3586 return (UINT)-1;
3588 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3589 return (UINT)-1;
3591 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3592 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3594 /* If the close item in the system menu change update the close button */
3595 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3597 if (menu->hSysMenuOwner != 0)
3599 RECT rc;
3600 POPUPMENU* parentMenu;
3602 /* Get the parent menu to access*/
3603 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3604 return (UINT)-1;
3606 /* Refresh the frame to reflect the change */
3607 GetWindowRect(parentMenu->hWnd, &rc);
3608 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3609 rc.bottom = 0;
3610 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3614 return oldflags;
3618 /*******************************************************************
3619 * GetMenuStringA (USER32.@)
3621 INT WINAPI GetMenuStringA(
3622 HMENU hMenu, /* [in] menuhandle */
3623 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3624 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3625 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3626 UINT wFlags /* [in] MF_ flags */
3628 MENUITEM *item;
3630 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3631 if (str && nMaxSiz) str[0] = '\0';
3632 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3633 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3634 return 0;
3636 if (!item->text) return 0;
3637 if (!str || !nMaxSiz) return strlenW(item->text);
3638 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3639 str[nMaxSiz-1] = 0;
3640 TRACE("returning %s\n", debugstr_a(str));
3641 return strlen(str);
3645 /*******************************************************************
3646 * GetMenuStringW (USER32.@)
3648 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3649 LPWSTR str, INT nMaxSiz, UINT wFlags )
3651 MENUITEM *item;
3653 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3654 if (str && nMaxSiz) str[0] = '\0';
3655 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3656 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3657 return 0;
3659 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3660 if( !(item->text)) {
3661 str[0] = 0;
3662 return 0;
3664 lstrcpynW( str, item->text, nMaxSiz );
3665 TRACE("returning %s\n", debugstr_w(str));
3666 return strlenW(str);
3670 /**********************************************************************
3671 * HiliteMenuItem (USER32.@)
3673 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3674 UINT wHilite )
3676 LPPOPUPMENU menu;
3677 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3678 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3679 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3680 if (menu->FocusedItem == wItemID) return TRUE;
3681 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3682 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3683 return TRUE;
3687 /**********************************************************************
3688 * GetMenuState (USER32.@)
3690 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3692 MENUITEM *item;
3693 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3694 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3695 debug_print_menuitem (" item: ", item, "");
3696 if (item->fType & MF_POPUP)
3698 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3699 if (!menu) return -1;
3700 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3702 else
3704 /* We used to (from way back then) mask the result to 0xff. */
3705 /* I don't know why and it seems wrong as the documented */
3706 /* return flag MF_SEPARATOR is outside that mask. */
3707 return (item->fType | item->fState);
3712 /**********************************************************************
3713 * GetMenuItemCount (USER32.@)
3715 INT WINAPI GetMenuItemCount( HMENU hMenu )
3717 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3718 if (!menu) return -1;
3719 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3720 return menu->nItems;
3724 /**********************************************************************
3725 * GetMenuItemID (USER32.@)
3727 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3729 MENUITEM * lpmi;
3731 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3732 if (lpmi->fType & MF_POPUP) return -1;
3733 return lpmi->wID;
3738 /*******************************************************************
3739 * InsertMenuW (USER32.@)
3741 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3742 UINT_PTR id, LPCWSTR str )
3744 MENUITEM *item;
3746 if (IS_STRING_ITEM(flags) && str)
3747 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3748 hMenu, pos, flags, id, debugstr_w(str) );
3749 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3750 hMenu, pos, flags, id, str );
3752 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3754 if (!(MENU_SetItemData( item, flags, id, str )))
3756 RemoveMenu( hMenu, pos, flags );
3757 return FALSE;
3760 item->hCheckBit = item->hUnCheckBit = 0;
3761 return TRUE;
3765 /*******************************************************************
3766 * InsertMenuA (USER32.@)
3768 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3769 UINT_PTR id, LPCSTR str )
3771 BOOL ret = FALSE;
3773 if (IS_STRING_ITEM(flags) && str)
3775 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3776 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3777 if (newstr)
3779 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3780 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3781 HeapFree( GetProcessHeap(), 0, newstr );
3783 return ret;
3785 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3789 /*******************************************************************
3790 * AppendMenuA (USER32.@)
3792 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3793 UINT_PTR id, LPCSTR data )
3795 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3799 /*******************************************************************
3800 * AppendMenuW (USER32.@)
3802 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3803 UINT_PTR id, LPCWSTR data )
3805 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3809 /**********************************************************************
3810 * RemoveMenu (USER32.@)
3812 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3814 LPPOPUPMENU menu;
3815 MENUITEM *item;
3817 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3818 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3819 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3821 /* Remove item */
3823 MENU_FreeItemData( item );
3825 if (--menu->nItems == 0)
3827 HeapFree( GetProcessHeap(), 0, menu->items );
3828 menu->items = NULL;
3830 else
3832 while(nPos < menu->nItems)
3834 *item = *(item+1);
3835 item++;
3836 nPos++;
3838 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3839 menu->nItems * sizeof(MENUITEM) );
3841 return TRUE;
3845 /**********************************************************************
3846 * DeleteMenu (USER32.@)
3848 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3850 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3851 if (!item) return FALSE;
3852 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3853 /* nPos is now the position of the item */
3854 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3855 return TRUE;
3859 /*******************************************************************
3860 * ModifyMenuW (USER32.@)
3862 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3863 UINT_PTR id, LPCWSTR str )
3865 MENUITEM *item;
3867 if (IS_STRING_ITEM(flags))
3868 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3869 else
3870 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3872 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3873 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3874 return MENU_SetItemData( item, flags, id, str );
3878 /*******************************************************************
3879 * ModifyMenuA (USER32.@)
3881 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3882 UINT_PTR id, LPCSTR str )
3884 BOOL ret = FALSE;
3886 if (IS_STRING_ITEM(flags) && str)
3888 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3889 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3890 if (newstr)
3892 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3893 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3894 HeapFree( GetProcessHeap(), 0, newstr );
3896 return ret;
3898 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3902 /**********************************************************************
3903 * CreatePopupMenu (USER32.@)
3905 HMENU WINAPI CreatePopupMenu(void)
3907 HMENU hmenu;
3908 POPUPMENU *menu;
3910 if (!(hmenu = CreateMenu())) return 0;
3911 menu = MENU_GetMenu( hmenu );
3912 menu->wFlags |= MF_POPUP;
3913 menu->bTimeToHide = FALSE;
3914 return hmenu;
3918 /**********************************************************************
3919 * GetMenuCheckMarkDimensions (USER.417)
3920 * GetMenuCheckMarkDimensions (USER32.@)
3922 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3924 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3928 /**********************************************************************
3929 * SetMenuItemBitmaps (USER32.@)
3931 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3932 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3934 MENUITEM *item;
3936 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3938 if (!hNewCheck && !hNewUnCheck)
3940 item->fState &= ~MF_USECHECKBITMAPS;
3942 else /* Install new bitmaps */
3944 item->hCheckBit = hNewCheck;
3945 item->hUnCheckBit = hNewUnCheck;
3946 item->fState |= MF_USECHECKBITMAPS;
3948 return TRUE;
3952 /**********************************************************************
3953 * CreateMenu (USER32.@)
3955 HMENU WINAPI CreateMenu(void)
3957 HMENU hMenu;
3958 LPPOPUPMENU menu;
3959 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3960 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3962 ZeroMemory(menu, sizeof(POPUPMENU));
3963 menu->wMagic = MENU_MAGIC;
3964 menu->FocusedItem = NO_SELECTED_ITEM;
3965 menu->bTimeToHide = FALSE;
3967 TRACE("return %p\n", hMenu );
3969 return hMenu;
3973 /**********************************************************************
3974 * DestroyMenu (USER32.@)
3976 BOOL WINAPI DestroyMenu( HMENU hMenu )
3978 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3980 TRACE("(%p)\n", hMenu);
3983 if (!lppop) return FALSE;
3985 lppop->wMagic = 0; /* Mark it as destroyed */
3987 /* DestroyMenu should not destroy system menu popup owner */
3988 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3990 DestroyWindow( lppop->hWnd );
3991 lppop->hWnd = 0;
3994 if (lppop->items) /* recursively destroy submenus */
3996 int i;
3997 MENUITEM *item = lppop->items;
3998 for (i = lppop->nItems; i > 0; i--, item++)
4000 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4001 MENU_FreeItemData( item );
4003 HeapFree( GetProcessHeap(), 0, lppop->items );
4005 USER_HEAP_FREE( hMenu );
4006 return TRUE;
4010 /**********************************************************************
4011 * GetSystemMenu (USER32.@)
4013 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4015 WND *wndPtr = WIN_GetPtr( hWnd );
4016 HMENU retvalue = 0;
4018 if (wndPtr == WND_DESKTOP) return 0;
4019 if (wndPtr == WND_OTHER_PROCESS)
4021 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4023 else if (wndPtr)
4025 if (wndPtr->hSysMenu && bRevert)
4027 DestroyMenu(wndPtr->hSysMenu);
4028 wndPtr->hSysMenu = 0;
4031 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4032 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4034 if( wndPtr->hSysMenu )
4036 POPUPMENU *menu;
4037 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4039 /* Store the dummy sysmenu handle to facilitate the refresh */
4040 /* of the close button if the SC_CLOSE item change */
4041 menu = MENU_GetMenu(retvalue);
4042 if ( menu )
4043 menu->hSysMenuOwner = wndPtr->hSysMenu;
4045 WIN_ReleasePtr( wndPtr );
4047 return bRevert ? 0 : retvalue;
4051 /*******************************************************************
4052 * SetSystemMenu (USER32.@)
4054 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4056 WND *wndPtr = WIN_GetPtr( hwnd );
4058 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4060 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4061 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4062 WIN_ReleasePtr( wndPtr );
4063 return TRUE;
4065 return FALSE;
4069 /**********************************************************************
4070 * GetMenu (USER32.@)
4072 HMENU WINAPI GetMenu( HWND hWnd )
4074 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4075 TRACE("for %p returning %p\n", hWnd, retvalue);
4076 return retvalue;
4079 /**********************************************************************
4080 * GetMenuBarInfo (USER32.@)
4082 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4084 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4085 return FALSE;
4088 /**********************************************************************
4089 * MENU_SetMenu
4091 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4092 * SetWindowPos call that would result if SetMenu were called directly.
4094 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4096 TRACE("(%p, %p);\n", hWnd, hMenu);
4098 if (hMenu && !IsMenu(hMenu))
4100 WARN("hMenu %p is not a menu handle\n", hMenu);
4101 return FALSE;
4103 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4104 return FALSE;
4106 hWnd = WIN_GetFullHandle( hWnd );
4107 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4109 if (hMenu != 0)
4111 LPPOPUPMENU lpmenu;
4113 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4115 lpmenu->hWnd = hWnd;
4116 lpmenu->Height = 0; /* Make sure we recalculate the size */
4118 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4119 return TRUE;
4123 /**********************************************************************
4124 * SetMenu (USER32.@)
4126 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4128 if(!MENU_SetMenu(hWnd, hMenu))
4129 return FALSE;
4131 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4132 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4133 return TRUE;
4137 /**********************************************************************
4138 * GetSubMenu (USER32.@)
4140 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4142 MENUITEM * lpmi;
4144 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4145 if (!(lpmi->fType & MF_POPUP)) return 0;
4146 return lpmi->hSubMenu;
4150 /**********************************************************************
4151 * DrawMenuBar (USER32.@)
4153 BOOL WINAPI DrawMenuBar( HWND hWnd )
4155 LPPOPUPMENU lppop;
4156 HMENU hMenu = GetMenu(hWnd);
4158 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4159 return FALSE;
4160 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4162 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4163 lppop->hwndOwner = hWnd;
4164 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4165 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4166 return TRUE;
4169 /***********************************************************************
4170 * DrawMenuBarTemp (USER32.@)
4172 * UNDOCUMENTED !!
4174 * called by W98SE desk.cpl Control Panel Applet
4176 * Not 100% sure about the param names, but close.
4178 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4180 LPPOPUPMENU lppop;
4181 UINT i,retvalue;
4182 HFONT hfontOld = 0;
4183 BOOL flat_menu = FALSE;
4185 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4187 if (!hMenu)
4188 hMenu = GetMenu(hwnd);
4190 if (!hFont)
4191 hFont = get_menu_font(FALSE);
4193 lppop = MENU_GetMenu( hMenu );
4194 if (lppop == NULL || lprect == NULL)
4196 retvalue = GetSystemMetrics(SM_CYMENU);
4197 goto END;
4200 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4202 hfontOld = SelectObject( hDC, hFont);
4204 if (lppop->Height == 0)
4205 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4207 lprect->bottom = lprect->top + lppop->Height;
4209 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4211 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4212 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4213 LineTo( hDC, lprect->right, lprect->bottom );
4215 if (lppop->nItems == 0)
4217 retvalue = GetSystemMetrics(SM_CYMENU);
4218 goto END;
4221 for (i = 0; i < lppop->nItems; i++)
4223 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4224 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4226 retvalue = lppop->Height;
4228 END:
4229 if (hfontOld) SelectObject (hDC, hfontOld);
4230 return retvalue;
4233 /***********************************************************************
4234 * EndMenu (USER.187)
4235 * EndMenu (USER32.@)
4237 void WINAPI EndMenu(void)
4239 /* if we are in the menu code, and it is active */
4240 if (!fEndMenu && top_popup)
4242 /* terminate the menu handling code */
4243 fEndMenu = TRUE;
4245 /* needs to be posted to wakeup the internal menu handler */
4246 /* which will now terminate the menu, in the event that */
4247 /* the main window was minimized, or lost focus, so we */
4248 /* don't end up with an orphaned menu */
4249 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4254 /***********************************************************************
4255 * LookupMenuHandle (USER.217)
4257 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4259 HMENU hmenu32 = HMENU_32(hmenu);
4260 UINT id32 = id;
4261 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4262 else return HMENU_16(hmenu32);
4266 /**********************************************************************
4267 * LoadMenu (USER.150)
4269 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4271 HRSRC16 hRsrc;
4272 HGLOBAL16 handle;
4273 HMENU16 hMenu;
4275 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4276 if (!name) return 0;
4278 instance = GetExePtr( instance );
4279 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4280 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4281 hMenu = LoadMenuIndirect16(LockResource16(handle));
4282 FreeResource16( handle );
4283 return hMenu;
4287 /*****************************************************************
4288 * LoadMenuA (USER32.@)
4290 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4292 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4293 if (!hrsrc) return 0;
4294 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4298 /*****************************************************************
4299 * LoadMenuW (USER32.@)
4301 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4303 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4304 if (!hrsrc) return 0;
4305 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4309 /**********************************************************************
4310 * LoadMenuIndirect (USER.220)
4312 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4314 HMENU hMenu;
4315 WORD version, offset;
4316 LPCSTR p = (LPCSTR)template;
4318 TRACE("(%p)\n", template );
4319 version = GET_WORD(p);
4320 p += sizeof(WORD);
4321 if (version)
4323 WARN("version must be 0 for Win16\n" );
4324 return 0;
4326 offset = GET_WORD(p);
4327 p += sizeof(WORD) + offset;
4328 if (!(hMenu = CreateMenu())) return 0;
4329 if (!MENU_ParseResource( p, hMenu, FALSE ))
4331 DestroyMenu( hMenu );
4332 return 0;
4334 return HMENU_16(hMenu);
4338 /**********************************************************************
4339 * LoadMenuIndirectW (USER32.@)
4341 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4343 HMENU hMenu;
4344 WORD version, offset;
4345 LPCSTR p = (LPCSTR)template;
4347 version = GET_WORD(p);
4348 p += sizeof(WORD);
4349 TRACE("%p, ver %d\n", template, version );
4350 switch (version)
4352 case 0: /* standard format is version of 0 */
4353 offset = GET_WORD(p);
4354 p += sizeof(WORD) + offset;
4355 if (!(hMenu = CreateMenu())) return 0;
4356 if (!MENU_ParseResource( p, hMenu, TRUE ))
4358 DestroyMenu( hMenu );
4359 return 0;
4361 return hMenu;
4362 case 1: /* extended format is version of 1 */
4363 offset = GET_WORD(p);
4364 p += sizeof(WORD) + offset;
4365 if (!(hMenu = CreateMenu())) return 0;
4366 if (!MENUEX_ParseResource( p, hMenu))
4368 DestroyMenu( hMenu );
4369 return 0;
4371 return hMenu;
4372 default:
4373 ERR("version %d not supported.\n", version);
4374 return 0;
4379 /**********************************************************************
4380 * LoadMenuIndirectA (USER32.@)
4382 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4384 return LoadMenuIndirectW( template );
4388 /**********************************************************************
4389 * IsMenu (USER32.@)
4391 BOOL WINAPI IsMenu(HMENU hmenu)
4393 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4395 if (!menu)
4397 SetLastError(ERROR_INVALID_MENU_HANDLE);
4398 return FALSE;
4400 return TRUE;
4403 /**********************************************************************
4404 * GetMenuItemInfo_common
4407 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4408 LPMENUITEMINFOW lpmii, BOOL unicode)
4410 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4412 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4414 if (!menu)
4415 return FALSE;
4417 if( lpmii->fMask & MIIM_TYPE) {
4418 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4419 WARN("invalid combination of fMask bits used\n");
4420 /* this does not happen on Win9x/ME */
4421 SetLastError( ERROR_INVALID_PARAMETER);
4422 return FALSE;
4424 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4425 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4426 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4427 if( lpmii->fType & MFT_BITMAP) {
4428 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4429 lpmii->cch = 0;
4430 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4431 /* this does not happen on Win9x/ME */
4432 lpmii->dwTypeData = 0;
4433 lpmii->cch = 0;
4437 /* copy the text string */
4438 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4439 if( !menu->text ) {
4440 if(lpmii->dwTypeData && lpmii->cch) {
4441 lpmii->cch = 0;
4442 if( unicode)
4443 *((WCHAR *)lpmii->dwTypeData) = 0;
4444 else
4445 *((CHAR *)lpmii->dwTypeData) = 0;
4447 } else {
4448 int len;
4449 if (unicode)
4451 len = strlenW(menu->text);
4452 if(lpmii->dwTypeData && lpmii->cch)
4453 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4455 else
4457 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4458 0, NULL, NULL ) - 1;
4459 if(lpmii->dwTypeData && lpmii->cch)
4460 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4461 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4462 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4464 /* if we've copied a substring we return its length */
4465 if(lpmii->dwTypeData && lpmii->cch)
4466 if (lpmii->cch <= len + 1)
4467 lpmii->cch--;
4468 else
4469 lpmii->cch = len;
4470 else {
4471 /* return length of string */
4472 /* not on Win9x/ME if fType & MFT_BITMAP */
4473 lpmii->cch = len;
4478 if (lpmii->fMask & MIIM_FTYPE)
4479 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4481 if (lpmii->fMask & MIIM_BITMAP)
4482 lpmii->hbmpItem = menu->hbmpItem;
4484 if (lpmii->fMask & MIIM_STATE)
4485 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4487 if (lpmii->fMask & MIIM_ID)
4488 lpmii->wID = menu->wID;
4490 if (lpmii->fMask & MIIM_SUBMENU)
4491 lpmii->hSubMenu = menu->hSubMenu;
4492 else {
4493 /* hSubMenu is always cleared
4494 * (not on Win9x/ME ) */
4495 lpmii->hSubMenu = 0;
4498 if (lpmii->fMask & MIIM_CHECKMARKS) {
4499 lpmii->hbmpChecked = menu->hCheckBit;
4500 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4502 if (lpmii->fMask & MIIM_DATA)
4503 lpmii->dwItemData = menu->dwItemData;
4505 return TRUE;
4508 /**********************************************************************
4509 * GetMenuItemInfoA (USER32.@)
4511 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4512 LPMENUITEMINFOA lpmii)
4514 BOOL ret;
4515 MENUITEMINFOA mii;
4516 if( lpmii->cbSize != sizeof( mii) &&
4517 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4518 SetLastError( ERROR_INVALID_PARAMETER);
4519 return FALSE;
4521 memcpy( &mii, lpmii, lpmii->cbSize);
4522 mii.cbSize = sizeof( mii);
4523 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4524 (LPMENUITEMINFOW)&mii, FALSE);
4525 mii.cbSize = lpmii->cbSize;
4526 memcpy( lpmii, &mii, mii.cbSize);
4527 return ret;
4530 /**********************************************************************
4531 * GetMenuItemInfoW (USER32.@)
4533 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4534 LPMENUITEMINFOW lpmii)
4536 BOOL ret;
4537 MENUITEMINFOW mii;
4538 if( lpmii->cbSize != sizeof( mii) &&
4539 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4540 SetLastError( ERROR_INVALID_PARAMETER);
4541 return FALSE;
4543 memcpy( &mii, lpmii, lpmii->cbSize);
4544 mii.cbSize = sizeof( mii);
4545 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4546 mii.cbSize = lpmii->cbSize;
4547 memcpy( lpmii, &mii, mii.cbSize);
4548 return ret;
4552 /* set a menu item text from a ASCII or Unicode string */
4553 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4555 if (!text)
4556 menu->text = NULL;
4557 else if (unicode)
4559 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4560 strcpyW( menu->text, text );
4562 else
4564 LPCSTR str = (LPCSTR)text;
4565 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4566 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4567 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4572 /**********************************************************************
4573 * SetMenuItemInfo_common
4576 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4577 const MENUITEMINFOW *lpmii,
4578 BOOL unicode)
4580 if (!menu) return FALSE;
4582 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4584 if (lpmii->fMask & MIIM_TYPE ) {
4585 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4586 WARN("invalid combination of fMask bits used\n");
4587 /* this does not happen on Win9x/ME */
4588 SetLastError( ERROR_INVALID_PARAMETER);
4589 return FALSE;
4592 /* Remove the old type bits and replace them with the new ones */
4593 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4594 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4596 if (IS_STRING_ITEM(menu->fType)) {
4597 HeapFree(GetProcessHeap(), 0, menu->text);
4598 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4599 } else if( (menu->fType) & MFT_BITMAP)
4600 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4603 if (lpmii->fMask & MIIM_FTYPE ) {
4604 if(( lpmii->fType & MFT_BITMAP)) {
4605 SetLastError( ERROR_INVALID_PARAMETER);
4606 return FALSE;
4608 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4609 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4611 if (lpmii->fMask & MIIM_STRING ) {
4612 /* free the string when used */
4613 HeapFree(GetProcessHeap(), 0, menu->text);
4614 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4617 if (lpmii->fMask & MIIM_STATE)
4619 /* Other menu items having MFS_DEFAULT are not converted
4620 to normal items */
4621 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4624 if (lpmii->fMask & MIIM_ID)
4625 menu->wID = lpmii->wID;
4627 if (lpmii->fMask & MIIM_SUBMENU) {
4628 menu->hSubMenu = lpmii->hSubMenu;
4629 if (menu->hSubMenu) {
4630 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4631 if (subMenu) {
4632 subMenu->wFlags |= MF_POPUP;
4633 menu->fType |= MF_POPUP;
4635 else {
4636 SetLastError( ERROR_INVALID_PARAMETER);
4637 return FALSE;
4640 else
4641 menu->fType &= ~MF_POPUP;
4644 if (lpmii->fMask & MIIM_CHECKMARKS)
4646 menu->hCheckBit = lpmii->hbmpChecked;
4647 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4649 if (lpmii->fMask & MIIM_DATA)
4650 menu->dwItemData = lpmii->dwItemData;
4652 if (lpmii->fMask & MIIM_BITMAP)
4653 menu->hbmpItem = lpmii->hbmpItem;
4655 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4656 menu->fType |= MFT_SEPARATOR;
4658 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4659 return TRUE;
4662 /**********************************************************************
4663 * SetMenuItemInfoA (USER32.@)
4665 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4666 const MENUITEMINFOA *lpmii)
4668 MENUITEMINFOA mii;
4670 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4672 if( lpmii->cbSize != sizeof( mii) &&
4673 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4674 SetLastError( ERROR_INVALID_PARAMETER);
4675 return FALSE;
4677 memcpy( &mii, lpmii, lpmii->cbSize);
4678 if( lpmii->cbSize != sizeof( mii)) {
4679 mii.cbSize = sizeof( mii);
4680 mii.hbmpItem = NULL;
4682 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4683 (const MENUITEMINFOW *)&mii, FALSE);
4686 /**********************************************************************
4687 * SetMenuItemInfoW (USER32.@)
4689 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4690 const MENUITEMINFOW *lpmii)
4692 MENUITEMINFOW mii;
4694 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4696 if( lpmii->cbSize != sizeof( mii) &&
4697 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4698 SetLastError( ERROR_INVALID_PARAMETER);
4699 return FALSE;
4701 memcpy( &mii, lpmii, lpmii->cbSize);
4702 if( lpmii->cbSize != sizeof( mii)) {
4703 mii.cbSize = sizeof( mii);
4704 mii.hbmpItem = NULL;
4706 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4707 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4710 /**********************************************************************
4711 * SetMenuDefaultItem (USER32.@)
4714 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4716 UINT i;
4717 POPUPMENU *menu;
4718 MENUITEM *item;
4720 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4722 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4724 /* reset all default-item flags */
4725 item = menu->items;
4726 for (i = 0; i < menu->nItems; i++, item++)
4728 item->fState &= ~MFS_DEFAULT;
4731 /* no default item */
4732 if ( -1 == uItem)
4734 return TRUE;
4737 item = menu->items;
4738 if ( bypos )
4740 if ( uItem >= menu->nItems ) return FALSE;
4741 item[uItem].fState |= MFS_DEFAULT;
4742 return TRUE;
4744 else
4746 for (i = 0; i < menu->nItems; i++, item++)
4748 if (item->wID == uItem)
4750 item->fState |= MFS_DEFAULT;
4751 return TRUE;
4756 return FALSE;
4759 /**********************************************************************
4760 * GetMenuDefaultItem (USER32.@)
4762 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4764 POPUPMENU *menu;
4765 MENUITEM * item;
4766 UINT i = 0;
4768 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4770 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4772 /* find default item */
4773 item = menu->items;
4775 /* empty menu */
4776 if (! item) return -1;
4778 while ( !( item->fState & MFS_DEFAULT ) )
4780 i++; item++;
4781 if (i >= menu->nItems ) return -1;
4784 /* default: don't return disabled items */
4785 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4787 /* search rekursiv when needed */
4788 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4790 UINT ret;
4791 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4792 if ( -1 != ret ) return ret;
4794 /* when item not found in submenu, return the popup item */
4796 return ( bypos ) ? i : item->wID;
4801 /**********************************************************************
4802 * InsertMenuItemA (USER32.@)
4804 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4805 const MENUITEMINFOA *lpmii)
4807 MENUITEM *item;
4808 MENUITEMINFOA mii;
4810 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4812 if( lpmii->cbSize != sizeof( mii) &&
4813 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4814 SetLastError( ERROR_INVALID_PARAMETER);
4815 return FALSE;
4817 memcpy( &mii, lpmii, lpmii->cbSize);
4818 if( lpmii->cbSize != sizeof( mii)) {
4819 mii.cbSize = sizeof( mii);
4820 mii.hbmpItem = NULL;
4823 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4824 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4828 /**********************************************************************
4829 * InsertMenuItemW (USER32.@)
4831 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4832 const MENUITEMINFOW *lpmii)
4834 MENUITEM *item;
4835 MENUITEMINFOW mii;
4837 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4839 if( lpmii->cbSize != sizeof( mii) &&
4840 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4841 SetLastError( ERROR_INVALID_PARAMETER);
4842 return FALSE;
4844 memcpy( &mii, lpmii, lpmii->cbSize);
4845 if( lpmii->cbSize != sizeof( mii)) {
4846 mii.cbSize = sizeof( mii);
4847 mii.hbmpItem = NULL;
4850 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4851 return SetMenuItemInfo_common(item, &mii, TRUE);
4854 /**********************************************************************
4855 * CheckMenuRadioItem (USER32.@)
4858 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4859 UINT first, UINT last, UINT check,
4860 UINT bypos)
4862 BOOL done = FALSE;
4863 UINT i;
4864 MENUITEM *mi_first = NULL, *mi_check;
4865 HMENU m_first, m_check;
4867 for (i = first; i <= last; i++)
4869 UINT pos = i;
4871 if (!mi_first)
4873 m_first = hMenu;
4874 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4875 if (!mi_first) continue;
4876 mi_check = mi_first;
4877 m_check = m_first;
4879 else
4881 m_check = hMenu;
4882 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4883 if (!mi_check) continue;
4886 if (m_first != m_check) continue;
4887 if (mi_check->fType == MFT_SEPARATOR) continue;
4889 if (i == check)
4891 mi_check->fType |= MFT_RADIOCHECK;
4892 mi_check->fState |= MFS_CHECKED;
4893 done = TRUE;
4895 else
4897 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4898 mi_check->fState &= ~MFS_CHECKED;
4902 return done;
4906 /**********************************************************************
4907 * GetMenuItemRect (USER32.@)
4909 * ATTENTION: Here, the returned values in rect are the screen
4910 * coordinates of the item just like if the menu was
4911 * always on the upper left side of the application.
4914 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4915 LPRECT rect)
4917 POPUPMENU *itemMenu;
4918 MENUITEM *item;
4919 HWND referenceHwnd;
4921 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4923 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4924 referenceHwnd = hwnd;
4926 if(!hwnd)
4928 itemMenu = MENU_GetMenu(hMenu);
4929 if (itemMenu == NULL)
4930 return FALSE;
4932 if(itemMenu->hWnd == 0)
4933 return FALSE;
4934 referenceHwnd = itemMenu->hWnd;
4937 if ((rect == NULL) || (item == NULL))
4938 return FALSE;
4940 *rect = item->rect;
4942 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4944 return TRUE;
4948 /**********************************************************************
4949 * SetMenuInfo (USER32.@)
4951 * FIXME
4952 * MIM_APPLYTOSUBMENUS
4953 * actually use the items to draw the menu
4955 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4957 POPUPMENU *menu;
4959 TRACE("(%p %p)\n", hMenu, lpmi);
4961 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4964 if (lpmi->fMask & MIM_BACKGROUND)
4965 menu->hbrBack = lpmi->hbrBack;
4967 if (lpmi->fMask & MIM_HELPID)
4968 menu->dwContextHelpID = lpmi->dwContextHelpID;
4970 if (lpmi->fMask & MIM_MAXHEIGHT)
4971 menu->cyMax = lpmi->cyMax;
4973 if (lpmi->fMask & MIM_MENUDATA)
4974 menu->dwMenuData = lpmi->dwMenuData;
4976 if (lpmi->fMask & MIM_STYLE)
4978 menu->dwStyle = lpmi->dwStyle;
4979 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4980 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4981 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4982 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
4985 return TRUE;
4987 return FALSE;
4990 /**********************************************************************
4991 * GetMenuInfo (USER32.@)
4993 * NOTES
4994 * win98/NT5.0
4997 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4998 { POPUPMENU *menu;
5000 TRACE("(%p %p)\n", hMenu, lpmi);
5002 if (lpmi && (menu = MENU_GetMenu(hMenu)))
5005 if (lpmi->fMask & MIM_BACKGROUND)
5006 lpmi->hbrBack = menu->hbrBack;
5008 if (lpmi->fMask & MIM_HELPID)
5009 lpmi->dwContextHelpID = menu->dwContextHelpID;
5011 if (lpmi->fMask & MIM_MAXHEIGHT)
5012 lpmi->cyMax = menu->cyMax;
5014 if (lpmi->fMask & MIM_MENUDATA)
5015 lpmi->dwMenuData = menu->dwMenuData;
5017 if (lpmi->fMask & MIM_STYLE)
5018 lpmi->dwStyle = menu->dwStyle;
5020 return TRUE;
5022 return FALSE;
5026 /**********************************************************************
5027 * SetMenuContextHelpId (USER32.@)
5029 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5031 LPPOPUPMENU menu;
5033 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5035 if ((menu = MENU_GetMenu(hMenu)))
5037 menu->dwContextHelpID = dwContextHelpID;
5038 return TRUE;
5040 return FALSE;
5044 /**********************************************************************
5045 * GetMenuContextHelpId (USER32.@)
5047 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5049 LPPOPUPMENU menu;
5051 TRACE("(%p)\n", hMenu);
5053 if ((menu = MENU_GetMenu(hMenu)))
5055 return menu->dwContextHelpID;
5057 return 0;
5060 /**********************************************************************
5061 * MenuItemFromPoint (USER32.@)
5063 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5065 POPUPMENU *menu = MENU_GetMenu(hMenu);
5066 UINT pos;
5068 /*FIXME: Do we have to handle hWnd here? */
5069 if (!menu) return -1;
5070 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5071 return pos;
5075 /**********************************************************************
5076 * translate_accelerator
5078 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5079 BYTE fVirt, WORD key, WORD cmd )
5081 INT mask = 0;
5082 UINT mesg = 0;
5084 if (wParam != key) return FALSE;
5086 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5087 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5088 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5090 if (message == WM_CHAR || message == WM_SYSCHAR)
5092 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5094 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5095 goto found;
5098 else
5100 if(fVirt & FVIRTKEY)
5102 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5103 wParam, 0xff & HIWORD(lParam));
5105 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5106 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5108 else
5110 if (!(lParam & 0x01000000)) /* no special_key */
5112 if ((fVirt & FALT) && (lParam & 0x20000000))
5113 { /* ^^ ALT pressed */
5114 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5115 goto found;
5120 return FALSE;
5122 found:
5123 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5124 mesg = 1;
5125 else
5127 HMENU hMenu, hSubMenu, hSysMenu;
5128 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5130 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5131 hSysMenu = get_win_sys_menu( hWnd );
5133 /* find menu item and ask application to initialize it */
5134 /* 1. in the system menu */
5135 hSubMenu = hSysMenu;
5136 nPos = cmd;
5137 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5139 if (GetCapture())
5140 mesg = 2;
5141 if (!IsWindowEnabled(hWnd))
5142 mesg = 3;
5143 else
5145 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5146 if(hSubMenu != hSysMenu)
5148 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5149 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5150 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5152 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5155 else /* 2. in the window's menu */
5157 hSubMenu = hMenu;
5158 nPos = cmd;
5159 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5161 if (GetCapture())
5162 mesg = 2;
5163 if (!IsWindowEnabled(hWnd))
5164 mesg = 3;
5165 else
5167 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5168 if(hSubMenu != hMenu)
5170 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5171 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5172 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5174 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5179 if (mesg == 0)
5181 if (uSysStat != (UINT)-1)
5183 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5184 mesg=4;
5185 else
5186 mesg=WM_SYSCOMMAND;
5188 else
5190 if (uStat != (UINT)-1)
5192 if (IsIconic(hWnd))
5193 mesg=5;
5194 else
5196 if (uStat & (MF_DISABLED|MF_GRAYED))
5197 mesg=6;
5198 else
5199 mesg=WM_COMMAND;
5202 else
5203 mesg=WM_COMMAND;
5208 if( mesg==WM_COMMAND )
5210 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5211 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5213 else if( mesg==WM_SYSCOMMAND )
5215 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5216 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5218 else
5220 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5221 * #0: unknown (please report!)
5222 * #1: for WM_KEYUP,WM_SYSKEYUP
5223 * #2: mouse is captured
5224 * #3: window is disabled
5225 * #4: it's a disabled system menu option
5226 * #5: it's a menu option, but window is iconic
5227 * #6: it's a menu option, but disabled
5229 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5230 if(mesg==0)
5231 ERR_(accel)(" unknown reason - please report!\n");
5233 return TRUE;
5236 /**********************************************************************
5237 * TranslateAcceleratorA (USER32.@)
5238 * TranslateAccelerator (USER32.@)
5240 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5242 /* YES, Accel16! */
5243 LPACCEL16 lpAccelTbl;
5244 int i;
5245 WPARAM wParam;
5247 if (!hWnd || !msg) return 0;
5249 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5251 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5252 return 0;
5255 wParam = msg->wParam;
5257 switch (msg->message)
5259 case WM_KEYDOWN:
5260 case WM_SYSKEYDOWN:
5261 break;
5263 case WM_CHAR:
5264 case WM_SYSCHAR:
5266 char ch = LOWORD(wParam);
5267 WCHAR wch;
5268 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5269 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5271 break;
5273 default:
5274 return 0;
5277 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5278 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5279 i = 0;
5282 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5283 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5284 return 1;
5285 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5287 return 0;
5290 /**********************************************************************
5291 * TranslateAcceleratorW (USER32.@)
5293 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5295 /* YES, Accel16! */
5296 LPACCEL16 lpAccelTbl;
5297 int i;
5299 if (!hWnd || !msg) return 0;
5301 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5303 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5304 return 0;
5307 switch (msg->message)
5309 case WM_KEYDOWN:
5310 case WM_SYSKEYDOWN:
5311 case WM_CHAR:
5312 case WM_SYSCHAR:
5313 break;
5315 default:
5316 return 0;
5319 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5320 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5321 i = 0;
5324 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5325 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5326 return 1;
5327 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5329 return 0;