Changes in crossover-wine-src-6.1.0 except for configure
[wine/hacks.git] / dlls / user32 / menu.c
blob9ad64c88c4c7cf55926c85fa9251f0837d2fafde
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
56 #include "wownt32.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "win.h"
60 #include "controls.h"
61 #include "user_private.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(menu);
65 WINE_DECLARE_DEBUG_CHANNEL(accel);
67 /* internal popup menu window messages */
69 #define MM_SETMENUHANDLE (WM_USER + 0)
70 #define MM_GETMENUHANDLE (WM_USER + 1)
72 /* Menu item structure */
73 typedef struct {
74 /* ----------- MENUITEMINFO Stuff ----------- */
75 UINT fType; /* Item type. */
76 UINT fState; /* Item state. */
77 UINT_PTR wID; /* Item id. */
78 HMENU hSubMenu; /* Pop-up menu. */
79 HBITMAP hCheckBit; /* Bitmap when checked. */
80 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
81 LPWSTR text; /* Item text. */
82 ULONG_PTR dwItemData; /* Application defined. */
83 LPWSTR dwTypeData; /* depends on fMask */
84 HBITMAP hbmpItem; /* bitmap */
85 /* ----------- Wine stuff ----------- */
86 RECT rect; /* Item area (relative to menu window) */
87 UINT xTab; /* X position of text after Tab */
88 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
89 * bitmap */
90 } MENUITEM;
92 /* Popup menu structure */
93 typedef struct {
94 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
95 WORD wMagic; /* Magic number */
96 WORD Width; /* Width of the whole menu */
97 WORD Height; /* Height of the whole menu */
98 UINT nItems; /* Number of items in the menu */
99 HWND hWnd; /* Window containing the menu */
100 MENUITEM *items; /* Array of menu items */
101 UINT FocusedItem; /* Currently focused item */
102 HWND hwndOwner; /* window receiving the messages for ownerdraw */
103 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
104 BOOL bScrolling; /* Scroll arrows are active */
105 UINT nScrollPos; /* Current scroll position */
106 UINT nTotalHeight; /* Total height of menu items inside menu */
107 /* ------------ MENUINFO members ------ */
108 DWORD dwStyle; /* Extended menu style */
109 UINT cyMax; /* max height of the whole menu, 0 is screen height */
110 HBRUSH hbrBack; /* brush for menu background */
111 DWORD dwContextHelpID;
112 DWORD dwMenuData; /* application defined value */
113 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
114 SIZE maxBmpSize; /* Maximum size of the bitmap items */
115 } POPUPMENU, *LPPOPUPMENU;
117 /* internal flags for menu tracking */
119 #define TF_ENDMENU 0x0001
120 #define TF_SUSPENDPOPUP 0x0002
121 #define TF_SKIPREMOVE 0x0004
123 typedef struct
125 UINT trackFlags;
126 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
127 HMENU hTopMenu; /* initial menu */
128 HWND hOwnerWnd; /* where notifications are sent */
129 POINT pt;
130 } MTRACKER;
132 #define MENU_MAGIC 0x554d /* 'MU' */
134 #define ITEM_PREV -1
135 #define ITEM_NEXT 1
137 /* Internal MENU_TrackMenu() flags */
138 #define TPM_INTERNAL 0xF0000000
139 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
140 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
141 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
143 /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
146 /* top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
150 /* (other menu->FocusedItem values give the position of the focused item) */
151 #define NO_SELECTED_ITEM 0xffff
153 #define MENU_ITEM_TYPE(flags) \
154 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
156 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
157 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
158 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
160 #define IS_SYSTEM_MENU(menu) \
161 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
163 #define MENUITEMINFO_TYPE_MASK \
164 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
165 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
166 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
167 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
168 #define STATE_MASK (~TYPE_MASK)
169 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
171 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
173 static SIZE menucharsize;
174 static UINT ODitemheight; /* default owner drawn item height */
176 /* Use global popup window because there's no way 2 menus can
177 * be tracked at the same time. */
178 static HWND top_popup;
180 /* Flag set by EndMenu() to force an exit from menu tracking */
181 static BOOL fEndMenu = FALSE;
183 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
185 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
187 /*********************************************************************
188 * menu class descriptor
190 const struct builtin_class_descr MENU_builtin_class =
192 (LPCSTR)POPUPMENU_CLASS_ATOM, /* name */
193 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
194 NULL, /* procA (winproc is Unicode only) */
195 PopupMenuWndProc, /* procW */
196 sizeof(HMENU), /* extra */
197 IDC_ARROW, /* cursor */
198 (HBRUSH)(COLOR_MENU+1) /* brush */
202 /***********************************************************************
203 * debug_print_menuitem
205 * Print a menuitem in readable form.
208 #define debug_print_menuitem(pre, mp, post) \
209 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
211 #define MENUOUT(text) \
212 TRACE("%s%s", (count++ ? "," : ""), (text))
214 #define MENUFLAG(bit,text) \
215 do { \
216 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
217 } while (0)
219 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
220 const char *postfix)
222 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
223 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
224 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
225 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
226 TRACE("%s ", prefix);
227 if (mp) {
228 UINT flags = mp->fType;
229 TRACE( "{ ID=0x%x", mp->wID);
230 if ( mp->hSubMenu)
231 TRACE( ", Sub=%p", mp->hSubMenu);
232 if (flags) {
233 int count = 0;
234 TRACE( ", fType=");
235 MENUFLAG( MFT_SEPARATOR, "sep");
236 MENUFLAG( MFT_OWNERDRAW, "own");
237 MENUFLAG( MFT_BITMAP, "bit");
238 MENUFLAG(MF_POPUP, "pop");
239 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240 MENUFLAG(MFT_MENUBREAK, "brk");
241 MENUFLAG(MFT_RADIOCHECK, "radio");
242 MENUFLAG(MFT_RIGHTORDER, "rorder");
243 MENUFLAG(MF_SYSMENU, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
245 if (flags)
246 TRACE( "+0x%x", flags);
248 flags = mp->fState;
249 if (flags) {
250 int count = 0;
251 TRACE( ", State=");
252 MENUFLAG(MFS_GRAYED, "grey");
253 MENUFLAG(MFS_DEFAULT, "default");
254 MENUFLAG(MFS_DISABLED, "dis");
255 MENUFLAG(MFS_CHECKED, "check");
256 MENUFLAG(MFS_HILITE, "hi");
257 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
258 MENUFLAG(MF_MOUSESELECT, "mouse");
259 if (flags)
260 TRACE( "+0x%x", flags);
262 if (mp->hCheckBit)
263 TRACE( ", Chk=%p", mp->hCheckBit);
264 if (mp->hUnCheckBit)
265 TRACE( ", Unc=%p", mp->hUnCheckBit);
266 if (mp->text)
267 TRACE( ", Text=%s", debugstr_w(mp->text));
268 if (mp->dwItemData)
269 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
270 if (mp->hbmpItem)
272 if( IS_MAGIC_BITMAP(mp->hbmpItem))
273 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
274 else
275 TRACE( ", hbitmap=%p", mp->hbmpItem);
277 TRACE( " }");
278 } else
279 TRACE( "NULL");
280 TRACE(" %s\n", postfix);
283 #undef MENUOUT
284 #undef MENUFLAG
287 /***********************************************************************
288 * MENU_GetMenu
290 * Validate the given menu handle and returns the menu structure pointer.
292 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
294 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
295 if (!menu || menu->wMagic != MENU_MAGIC)
297 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
298 menu = NULL;
300 return menu;
303 /***********************************************************************
304 * get_win_sys_menu
306 * Get the system menu of a window
308 static HMENU get_win_sys_menu( HWND hwnd )
310 HMENU ret = 0;
311 WND *win = WIN_GetPtr( hwnd );
312 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
314 ret = win->hSysMenu;
315 WIN_ReleasePtr( win );
317 return ret;
320 /***********************************************************************
321 * get_menu_font
323 static HFONT get_menu_font( BOOL bold )
325 static HFONT hMenuFont, hMenuFontBold;
327 HFONT ret = bold ? hMenuFontBold : hMenuFont;
329 if (!ret)
331 NONCLIENTMETRICSW ncm;
332 HFONT prev;
334 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
335 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
337 if (bold)
339 ncm.lfMenuFont.lfWeight += 300;
340 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
342 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
343 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
344 ret, NULL );
345 if (prev)
347 /* another thread beat us to it */
348 DeleteObject( ret );
349 ret = prev;
352 return ret;
355 /***********************************************************************
356 * get_arrow_bitmap
358 static HBITMAP get_arrow_bitmap(void)
360 static HBITMAP arrow_bitmap;
362 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
363 return arrow_bitmap;
366 /***********************************************************************
367 * get_down_arrow_bitmap
369 static HBITMAP get_down_arrow_bitmap(void)
371 static HBITMAP arrow_bitmap;
373 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
374 return arrow_bitmap;
377 /***********************************************************************
378 * get_down_arrow_inactive_bitmap
380 static HBITMAP get_down_arrow_inactive_bitmap(void)
382 static HBITMAP arrow_bitmap;
384 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
385 return arrow_bitmap;
388 /***********************************************************************
389 * get_up_arrow_bitmap
391 static HBITMAP get_up_arrow_bitmap(void)
393 static HBITMAP arrow_bitmap;
395 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
396 return arrow_bitmap;
399 /***********************************************************************
400 * get_up_arrow_inactive_bitmap
402 static HBITMAP get_up_arrow_inactive_bitmap(void)
404 static HBITMAP arrow_bitmap;
406 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
407 return arrow_bitmap;
410 /***********************************************************************
411 * MENU_CopySysPopup
413 * Return the default system menu.
415 static HMENU MENU_CopySysPopup(void)
417 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
418 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
420 if( hMenu ) {
421 POPUPMENU* menu = MENU_GetMenu(hMenu);
422 menu->wFlags |= MF_SYSMENU | MF_POPUP;
423 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
425 else
426 ERR("Unable to load default system menu\n" );
428 TRACE("returning %p.\n", hMenu );
430 return hMenu;
434 /**********************************************************************
435 * MENU_GetSysMenu
437 * Create a copy of the system menu. System menu in Windows is
438 * a special menu bar with the single entry - system menu popup.
439 * This popup is presented to the outside world as a "system menu".
440 * However, the real system menu handle is sometimes seen in the
441 * WM_MENUSELECT parameters (and Word 6 likes it this way).
443 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
445 HMENU hMenu;
447 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
448 if ((hMenu = CreateMenu()))
450 POPUPMENU *menu = MENU_GetMenu(hMenu);
451 menu->wFlags = MF_SYSMENU;
452 menu->hWnd = WIN_GetFullHandle( hWnd );
453 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
455 if (!hPopupMenu)
456 hPopupMenu = MENU_CopySysPopup();
458 if (hPopupMenu)
460 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
461 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
463 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
464 (UINT_PTR)hPopupMenu, NULL );
466 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
467 menu->items[0].fState = 0;
468 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
470 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
471 return hMenu;
473 DestroyMenu( hMenu );
475 ERR("failed to load system menu!\n");
476 return 0;
480 /***********************************************************************
481 * MENU_InitSysMenuPopup
483 * Grey the appropriate items in System menu.
485 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
487 BOOL gray;
489 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
490 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
491 gray = ((style & WS_MAXIMIZE) != 0);
492 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
493 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
494 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
496 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
497 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
498 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
499 gray = (clsStyle & CS_NOCLOSE) != 0;
501 /* The menu item must keep its state if it's disabled */
502 if(gray)
503 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
507 /******************************************************************************
509 * UINT MENU_GetStartOfNextColumn(
510 * HMENU hMenu )
512 *****************************************************************************/
514 static UINT MENU_GetStartOfNextColumn(
515 HMENU hMenu )
517 POPUPMENU *menu = MENU_GetMenu(hMenu);
518 UINT i;
520 if(!menu)
521 return NO_SELECTED_ITEM;
523 i = menu->FocusedItem + 1;
524 if( i == NO_SELECTED_ITEM )
525 return i;
527 for( ; i < menu->nItems; ++i ) {
528 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
529 return i;
532 return NO_SELECTED_ITEM;
536 /******************************************************************************
538 * UINT MENU_GetStartOfPrevColumn(
539 * HMENU hMenu )
541 *****************************************************************************/
543 static UINT MENU_GetStartOfPrevColumn(
544 HMENU hMenu )
546 POPUPMENU *menu = MENU_GetMenu(hMenu);
547 UINT i;
549 if( !menu )
550 return NO_SELECTED_ITEM;
552 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
553 return NO_SELECTED_ITEM;
555 /* Find the start of the column */
557 for(i = menu->FocusedItem; i != 0 &&
558 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
559 --i); /* empty */
561 if(i == 0)
562 return NO_SELECTED_ITEM;
564 for(--i; i != 0; --i) {
565 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
566 break;
569 TRACE("ret %d.\n", i );
571 return i;
576 /***********************************************************************
577 * MENU_FindItem
579 * Find a menu item. Return a pointer on the item, and modifies *hmenu
580 * in case the item was in a sub-menu.
582 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
584 POPUPMENU *menu;
585 MENUITEM *fallback = NULL;
586 UINT fallback_pos = 0;
587 UINT i;
589 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
590 if (wFlags & MF_BYPOSITION)
592 if (*nPos >= menu->nItems) return NULL;
593 return &menu->items[*nPos];
595 else
597 MENUITEM *item = menu->items;
598 for (i = 0; i < menu->nItems; i++, item++)
600 if (item->fType & MF_POPUP)
602 HMENU hsubmenu = item->hSubMenu;
603 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
604 if (subitem)
606 *hmenu = hsubmenu;
607 return subitem;
609 else if (item->wID == *nPos)
611 /* fallback to this item if nothing else found */
612 fallback_pos = i;
613 fallback = item;
616 else if (item->wID == *nPos)
618 *nPos = i;
619 return item;
624 if (fallback)
625 *nPos = fallback_pos;
627 return fallback;
630 /***********************************************************************
631 * MENU_FindSubMenu
633 * Find a Sub menu. Return the position of the submenu, and modifies
634 * *hmenu in case it is found in another sub-menu.
635 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
637 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
639 POPUPMENU *menu;
640 UINT i;
641 MENUITEM *item;
642 if (((*hmenu)==(HMENU)0xffff) ||
643 (!(menu = MENU_GetMenu(*hmenu))))
644 return NO_SELECTED_ITEM;
645 item = menu->items;
646 for (i = 0; i < menu->nItems; i++, item++) {
647 if(!(item->fType & MF_POPUP)) continue;
648 if (item->hSubMenu == hSubTarget) {
649 return i;
651 else {
652 HMENU hsubmenu = item->hSubMenu;
653 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
654 if (pos != NO_SELECTED_ITEM) {
655 *hmenu = hsubmenu;
656 return pos;
660 return NO_SELECTED_ITEM;
663 /***********************************************************************
664 * MENU_FreeItemData
666 static void MENU_FreeItemData( MENUITEM* item )
668 /* delete text */
669 HeapFree( GetProcessHeap(), 0, item->text );
672 /***********************************************************************
673 * MENU_AdjustMenuItemRect
675 * Adjust menu item rectangle according to scrolling state.
677 static void
678 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
680 if (menu->bScrolling)
682 UINT arrow_bitmap_width, arrow_bitmap_height;
683 BITMAP bmp;
685 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
686 arrow_bitmap_width = bmp.bmWidth;
687 arrow_bitmap_height = bmp.bmHeight;
688 rect->top += arrow_bitmap_height - menu->nScrollPos;
689 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
694 /***********************************************************************
695 * MENU_FindItemByCoords
697 * Find the item at the specified coordinates (screen coords). Does
698 * not work for child windows and therefore should not be called for
699 * an arbitrary system menu.
701 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
702 POINT pt, UINT *pos )
704 MENUITEM *item;
705 UINT i;
706 RECT wrect;
707 RECT rect;
709 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
710 pt.x -= wrect.left;pt.y -= wrect.top;
711 item = menu->items;
712 for (i = 0; i < menu->nItems; i++, item++)
714 rect = item->rect;
715 MENU_AdjustMenuItemRect(menu, &rect);
716 if ((pt.x >= rect.left) && (pt.x < rect.right) &&
717 (pt.y >= rect.top) && (pt.y < rect.bottom))
719 if (pos) *pos = i;
720 return item;
723 return NULL;
727 /***********************************************************************
728 * MENU_FindItemByKey
730 * Find the menu item selected by a key press.
731 * Return item id, -1 if none, -2 if we should close the menu.
733 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
734 WCHAR key, BOOL forceMenuChar )
736 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
738 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
740 if (hmenu)
742 POPUPMENU *menu = MENU_GetMenu( hmenu );
743 MENUITEM *item = menu->items;
744 LRESULT menuchar;
746 if( !forceMenuChar )
748 UINT i;
750 for (i = 0; i < menu->nItems; i++, item++)
752 if( item->text)
754 WCHAR *p = item->text - 2;
757 p = strchrW (p + 2, '&');
759 while (p != NULL && p [1] == '&');
760 if (p && (toupperW(p[1]) == toupperW(key))) return i;
764 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
765 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
766 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
767 if (HIWORD(menuchar) == 1) return (UINT)(-2);
769 return (UINT)(-1);
773 /***********************************************************************
774 * MENU_GetBitmapItemSize
776 * Get the size of a bitmap item.
778 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
779 HWND hwndOwner)
781 BITMAP bm;
782 HBITMAP bmp = lpitem->hbmpItem;
784 size->cx = size->cy = 0;
786 /* check if there is a magic menu item associated with this item */
787 switch( (INT_PTR) bmp )
789 case (INT_PTR)HBMMENU_CALLBACK:
791 MEASUREITEMSTRUCT measItem;
792 measItem.CtlType = ODT_MENU;
793 measItem.CtlID = 0;
794 measItem.itemID = lpitem->wID;
795 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
796 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
797 measItem.itemData = lpitem->dwItemData;
798 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
799 size->cx = measItem.itemWidth;
800 size->cy = measItem.itemHeight;
801 return;
803 break;
804 case (INT_PTR)HBMMENU_SYSTEM:
805 if (lpitem->dwItemData)
807 bmp = (HBITMAP)lpitem->dwItemData;
808 break;
810 /* fall through */
811 case (INT_PTR)HBMMENU_MBAR_RESTORE:
812 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
813 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
814 case (INT_PTR)HBMMENU_MBAR_CLOSE:
815 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
816 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
817 size->cy = size->cx;
818 return;
819 case (INT_PTR)HBMMENU_POPUP_CLOSE:
820 case (INT_PTR)HBMMENU_POPUP_RESTORE:
821 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
822 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
823 FIXME("Magic %p not implemented\n", bmp );
824 return;
826 if (GetObjectW(bmp, sizeof(bm), &bm ))
828 size->cx = bm.bmWidth;
829 size->cy = bm.bmHeight;
833 /***********************************************************************
834 * MENU_DrawBitmapItem
836 * Draw a bitmap item.
838 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
839 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
841 BITMAP bm;
842 DWORD rop;
843 HDC hdcMem;
844 HBITMAP bmp;
845 int w = rect->right - rect->left;
846 int h = rect->bottom - rect->top;
847 int bmp_xoffset = 0;
848 int left, top;
849 HBITMAP hbmToDraw = lpitem->hbmpItem;
850 bmp = hbmToDraw;
852 /* Check if there is a magic menu item associated with this item */
853 if (IS_MAGIC_BITMAP(hbmToDraw))
855 UINT flags = 0;
856 RECT r;
858 switch((INT_PTR)hbmToDraw)
860 case (INT_PTR)HBMMENU_SYSTEM:
861 if (lpitem->dwItemData)
863 bmp = (HBITMAP)lpitem->dwItemData;
864 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
866 else
868 static HBITMAP hBmpSysMenu;
870 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
871 bmp = hBmpSysMenu;
872 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
873 /* only use right half of the bitmap */
874 bmp_xoffset = bm.bmWidth / 2;
875 bm.bmWidth -= bmp_xoffset;
877 goto got_bitmap;
878 case (INT_PTR)HBMMENU_MBAR_RESTORE:
879 flags = DFCS_CAPTIONRESTORE;
880 break;
881 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
882 flags = DFCS_CAPTIONMIN;
883 break;
884 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
885 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
886 break;
887 case (INT_PTR)HBMMENU_MBAR_CLOSE:
888 flags = DFCS_CAPTIONCLOSE;
889 break;
890 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
891 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
892 break;
893 case (INT_PTR)HBMMENU_CALLBACK:
895 DRAWITEMSTRUCT drawItem;
896 drawItem.CtlType = ODT_MENU;
897 drawItem.CtlID = 0;
898 drawItem.itemID = lpitem->wID;
899 drawItem.itemAction = odaction;
900 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
901 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
902 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
903 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
904 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
905 drawItem.hwndItem = (HWND)hmenu;
906 drawItem.hDC = hdc;
907 drawItem.itemData = lpitem->dwItemData;
908 drawItem.rcItem = *rect;
909 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
910 return;
912 break;
913 case (INT_PTR)HBMMENU_POPUP_CLOSE:
914 case (INT_PTR)HBMMENU_POPUP_RESTORE:
915 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
916 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
917 default:
918 FIXME("Magic %p not implemented\n", hbmToDraw);
919 return;
921 r = *rect;
922 InflateRect( &r, -1, -1 );
923 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
924 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
925 return;
928 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
930 got_bitmap:
931 hdcMem = CreateCompatibleDC( hdc );
932 SelectObject( hdcMem, bmp );
934 /* handle fontsize > bitmap_height */
935 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
936 left=rect->left;
937 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
938 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
939 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
940 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
941 DeleteDC( hdcMem );
945 /***********************************************************************
946 * MENU_CalcItemSize
948 * Calculate the size of the menu item and store it in lpitem->rect.
950 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
951 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
953 WCHAR *p;
954 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
955 UINT arrow_bitmap_width;
956 BITMAP bm;
957 INT itemheight;
959 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
960 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
961 (menuBar ? " (MenuBar)" : ""));
963 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
964 arrow_bitmap_width = bm.bmWidth;
966 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
967 if( !menucharsize.cx ) {
968 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
969 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
970 * but it is unlikely an application will depend on that */
971 ODitemheight = HIWORD( GetDialogBaseUnits());
974 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
976 if (lpitem->fType & MF_OWNERDRAW)
978 MEASUREITEMSTRUCT mis;
979 mis.CtlType = ODT_MENU;
980 mis.CtlID = 0;
981 mis.itemID = lpitem->wID;
982 mis.itemData = lpitem->dwItemData;
983 mis.itemHeight = ODitemheight;
984 mis.itemWidth = 0;
985 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
986 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
987 * width of a menufont character to the width of an owner-drawn menu.
989 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
990 if (menuBar) {
991 /* under at least win95 you seem to be given a standard
992 height for the menu and the height value is ignored */
993 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
994 } else
995 lpitem->rect.bottom += mis.itemHeight;
997 TRACE("id=%04x size=%dx%d\n",
998 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
999 lpitem->rect.bottom-lpitem->rect.top);
1000 return;
1003 if (lpitem->fType & MF_SEPARATOR)
1005 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1006 if( !menuBar)
1007 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1008 return;
1011 itemheight = 0;
1012 lpitem->xTab = 0;
1014 if (!menuBar) {
1015 if (lpitem->hbmpItem) {
1016 SIZE size;
1018 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1019 /* Keep the size of the bitmap in callback mode to be able
1020 * to draw it correctly */
1021 lpitem->bmpsize = size;
1022 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1023 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1024 lpitem->rect.right += size.cx + 2;
1025 itemheight = size.cy + 2;
1027 if( !(lppop->dwStyle & MNS_NOCHECK))
1028 lpitem->rect.right += check_bitmap_width;
1029 lpitem->rect.right += 4 + menucharsize.cx;
1030 lpitem->xTab = lpitem->rect.right;
1031 lpitem->rect.right += arrow_bitmap_width;
1032 } else if (lpitem->hbmpItem) { /* menuBar */
1033 SIZE size;
1035 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1036 lpitem->bmpsize = size;
1037 lpitem->rect.right += size.cx;
1038 if( lpitem->text) lpitem->rect.right += 2;
1039 itemheight = size.cy;
1042 /* it must be a text item - unless it's the system menu */
1043 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1044 HFONT hfontOld = NULL;
1045 RECT rc = lpitem->rect;
1046 LONG txtheight, txtwidth;
1048 if ( lpitem->fState & MFS_DEFAULT ) {
1049 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1051 if (menuBar) {
1052 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1053 DT_SINGLELINE|DT_CALCRECT);
1054 lpitem->rect.right += rc.right - rc.left;
1055 itemheight = max( max( itemheight, txtheight),
1056 GetSystemMetrics( SM_CYMENU) - 1);
1057 lpitem->rect.right += 2 * menucharsize.cx;
1058 } else {
1059 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1060 RECT tmprc = rc;
1061 LONG tmpheight;
1062 int n = (int)( p - lpitem->text);
1063 /* Item contains a tab (only meaningful in popup menus) */
1064 /* get text size before the tab */
1065 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1066 DT_SINGLELINE|DT_CALCRECT);
1067 txtwidth = rc.right - rc.left;
1068 p += 1; /* advance past the Tab */
1069 /* get text size after the tab */
1070 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1071 DT_SINGLELINE|DT_CALCRECT);
1072 lpitem->xTab += txtwidth;
1073 txtheight = max( txtheight, tmpheight);
1074 txtwidth += menucharsize.cx + /* space for the tab */
1075 tmprc.right - tmprc.left; /* space for the short cut */
1076 } else {
1077 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1078 DT_SINGLELINE|DT_CALCRECT);
1079 txtwidth = rc.right - rc.left;
1080 lpitem->xTab += txtwidth;
1082 lpitem->rect.right += 2 + txtwidth;
1083 itemheight = max( itemheight,
1084 max( txtheight + 2, menucharsize.cy + 4));
1086 if (hfontOld) SelectObject (hdc, hfontOld);
1087 } else if( menuBar) {
1088 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1090 lpitem->rect.bottom += itemheight;
1091 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1095 /***********************************************************************
1096 * MENU_GetMaxPopupHeight
1098 static UINT
1099 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1101 if (lppop->cyMax)
1102 return lppop->cyMax;
1103 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1107 /***********************************************************************
1108 * MENU_PopupMenuCalcSize
1110 * Calculate the size of a popup menu.
1112 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1114 MENUITEM *lpitem;
1115 HDC hdc;
1116 int start, i;
1117 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1119 lppop->Width = lppop->Height = 0;
1120 if (lppop->nItems == 0) return;
1121 hdc = GetDC( 0 );
1123 SelectObject( hdc, get_menu_font(FALSE));
1125 start = 0;
1126 maxX = 2 + 1;
1128 lppop->maxBmpSize.cx = 0;
1129 lppop->maxBmpSize.cy = 0;
1131 while (start < lppop->nItems)
1133 lpitem = &lppop->items[start];
1134 orgX = maxX;
1135 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1136 orgX += MENU_COL_SPACE;
1137 orgY = MENU_TOP_MARGIN;
1139 maxTab = maxTabWidth = 0;
1140 /* Parse items until column break or end of menu */
1141 for (i = start; i < lppop->nItems; i++, lpitem++)
1143 if ((i != start) &&
1144 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1146 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1147 maxX = max( maxX, lpitem->rect.right );
1148 orgY = lpitem->rect.bottom;
1149 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1151 maxTab = max( maxTab, lpitem->xTab );
1152 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1156 /* Finish the column (set all items to the largest width found) */
1157 maxX = max( maxX, maxTab + maxTabWidth );
1158 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1160 lpitem->rect.right = maxX;
1161 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1162 lpitem->xTab = maxTab;
1165 lppop->Height = max( lppop->Height, orgY );
1168 lppop->Width = maxX;
1170 /* space for 3d border */
1171 lppop->Height += MENU_BOTTOM_MARGIN;
1172 lppop->Width += 2;
1174 /* Adjust popup height if it exceeds maximum */
1175 maxHeight = MENU_GetMaxPopupHeight(lppop);
1176 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1177 if (lppop->Height >= maxHeight)
1179 lppop->Height = maxHeight;
1180 lppop->bScrolling = TRUE;
1182 else
1184 lppop->bScrolling = FALSE;
1187 ReleaseDC( 0, hdc );
1191 /***********************************************************************
1192 * MENU_MenuBarCalcSize
1194 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1195 * height is off by 1 pixel which causes lengthy window relocations when
1196 * active document window is maximized/restored.
1198 * Calculate the size of the menu bar.
1200 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1201 LPPOPUPMENU lppop, HWND hwndOwner )
1203 MENUITEM *lpitem;
1204 int start, i, orgX, orgY, maxY, helpPos;
1206 if ((lprect == NULL) || (lppop == NULL)) return;
1207 if (lppop->nItems == 0) return;
1208 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1209 lppop->Width = lprect->right - lprect->left;
1210 lppop->Height = 0;
1211 maxY = lprect->top+1;
1212 start = 0;
1213 helpPos = -1;
1214 lppop->maxBmpSize.cx = 0;
1215 lppop->maxBmpSize.cy = 0;
1216 while (start < lppop->nItems)
1218 lpitem = &lppop->items[start];
1219 orgX = lprect->left;
1220 orgY = maxY;
1222 /* Parse items until line break or end of menu */
1223 for (i = start; i < lppop->nItems; i++, lpitem++)
1225 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1226 if ((i != start) &&
1227 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1229 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1230 debug_print_menuitem (" item: ", lpitem, "");
1231 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1233 if (lpitem->rect.right > lprect->right)
1235 if (i != start) break;
1236 else lpitem->rect.right = lprect->right;
1238 maxY = max( maxY, lpitem->rect.bottom );
1239 orgX = lpitem->rect.right;
1242 /* Finish the line (set all items to the largest height found) */
1243 while (start < i) lppop->items[start++].rect.bottom = maxY;
1246 lprect->bottom = maxY;
1247 lppop->Height = lprect->bottom - lprect->top;
1249 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1250 /* the last item (if several lines, only move the last line) */
1251 lpitem = &lppop->items[lppop->nItems-1];
1252 orgY = lpitem->rect.top;
1253 orgX = lprect->right;
1254 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1255 if ( (helpPos==-1) || (helpPos>i) )
1256 break; /* done */
1257 if (lpitem->rect.top != orgY) break; /* Other line */
1258 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1259 lpitem->rect.left += orgX - lpitem->rect.right;
1260 lpitem->rect.right = orgX;
1261 orgX = lpitem->rect.left;
1266 /***********************************************************************
1267 * MENU_DrawScrollArrows
1269 * Draw scroll arrows.
1271 static void
1272 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1274 HDC hdcMem = CreateCompatibleDC(hdc);
1275 HBITMAP hOrigBitmap;
1276 UINT arrow_bitmap_width, arrow_bitmap_height;
1277 BITMAP bmp;
1278 RECT rect;
1280 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1281 arrow_bitmap_width = bmp.bmWidth;
1282 arrow_bitmap_height = bmp.bmHeight;
1285 if (lppop->nScrollPos)
1286 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1287 else
1288 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1289 rect.left = 0;
1290 rect.top = 0;
1291 rect.right = lppop->Width;
1292 rect.bottom = arrow_bitmap_height;
1293 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1294 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1295 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1296 rect.top = lppop->Height - arrow_bitmap_height;
1297 rect.bottom = lppop->Height;
1298 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1299 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1300 SelectObject(hdcMem, get_down_arrow_bitmap());
1301 else
1302 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1303 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1304 lppop->Height - arrow_bitmap_height,
1305 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1306 SelectObject(hdcMem, hOrigBitmap);
1307 DeleteDC(hdcMem);
1311 /***********************************************************************
1312 * draw_popup_arrow
1314 * Draws the popup-menu arrow.
1316 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1317 UINT arrow_bitmap_height)
1319 HDC hdcMem = CreateCompatibleDC( hdc );
1320 HBITMAP hOrigBitmap;
1322 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1323 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1324 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1325 arrow_bitmap_width, arrow_bitmap_height,
1326 hdcMem, 0, 0, SRCCOPY );
1327 SelectObject( hdcMem, hOrigBitmap );
1328 DeleteDC( hdcMem );
1330 /***********************************************************************
1331 * MENU_DrawMenuItem
1333 * Draw a single menu item.
1335 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1336 UINT height, BOOL menuBar, UINT odaction )
1338 RECT rect;
1339 BOOL flat_menu = FALSE;
1340 int bkgnd;
1341 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1342 POPUPMENU *menu = MENU_GetMenu(hmenu);
1343 RECT bmprc;
1345 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1347 if (!menuBar) {
1348 BITMAP bmp;
1349 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1350 arrow_bitmap_width = bmp.bmWidth;
1351 arrow_bitmap_height = bmp.bmHeight;
1354 if (lpitem->fType & MF_SYSMENU)
1356 if( !IsIconic(hwnd) )
1357 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1358 return;
1361 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1362 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1364 /* Setup colors */
1366 if (lpitem->fState & MF_HILITE)
1368 if(menuBar && !flat_menu) {
1369 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1370 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1371 } else {
1372 if(lpitem->fState & MF_GRAYED)
1373 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1374 else
1375 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1376 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1379 else
1381 if (lpitem->fState & MF_GRAYED)
1382 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1383 else
1384 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1385 SetBkColor( hdc, GetSysColor( bkgnd ) );
1388 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1389 rect = lpitem->rect;
1390 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1392 if (lpitem->fType & MF_OWNERDRAW)
1395 ** Experimentation under Windows reveals that an owner-drawn
1396 ** menu is given the rectangle which includes the space it requested
1397 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1398 ** and a popup-menu arrow. This is the value of lpitem->rect.
1399 ** Windows will leave all drawing to the application except for
1400 ** the popup-menu arrow. Windows always draws that itself, after
1401 ** the menu owner has finished drawing.
1403 DRAWITEMSTRUCT dis;
1405 dis.CtlType = ODT_MENU;
1406 dis.CtlID = 0;
1407 dis.itemID = lpitem->wID;
1408 dis.itemData = lpitem->dwItemData;
1409 dis.itemState = 0;
1410 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1411 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1412 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1413 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1414 dis.hwndItem = (HWND)hmenu;
1415 dis.hDC = hdc;
1416 dis.rcItem = rect;
1417 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1418 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1419 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1420 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1421 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1422 /* Draw the popup-menu arrow */
1423 if (lpitem->fType & MF_POPUP)
1424 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1425 arrow_bitmap_height);
1426 return;
1429 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1431 if (lpitem->fState & MF_HILITE)
1433 if (flat_menu)
1435 InflateRect (&rect, -1, -1);
1436 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1437 InflateRect (&rect, 1, 1);
1438 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1440 else
1442 if(menuBar)
1443 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1444 else
1445 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1448 else
1449 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1451 SetBkMode( hdc, TRANSPARENT );
1453 /* vertical separator */
1454 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1456 HPEN oldPen;
1457 RECT rc = rect;
1459 rc.left -= MENU_COL_SPACE / 2 + 1;
1460 rc.top = 3;
1461 rc.bottom = height - 3;
1462 if (flat_menu)
1464 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1465 MoveToEx( hdc, rc.left, rc.top, NULL );
1466 LineTo( hdc, rc.left, rc.bottom );
1467 SelectObject( hdc, oldPen );
1469 else
1470 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1473 /* horizontal separator */
1474 if (lpitem->fType & MF_SEPARATOR)
1476 HPEN oldPen;
1477 RECT rc = rect;
1479 rc.left++;
1480 rc.right--;
1481 rc.top = ( rc.top + rc.bottom) / 2;
1482 if (flat_menu)
1484 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1485 MoveToEx( hdc, rc.left, rc.top, NULL );
1486 LineTo( hdc, rc.right, rc.top );
1487 SelectObject( hdc, oldPen );
1489 else
1490 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1491 return;
1494 /* helper lines for debugging */
1495 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1496 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1497 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1498 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1501 if (lpitem->hbmpItem) {
1502 /* calculate the bitmap rectangle in coordinates relative
1503 * to the item rectangle */
1504 if( menuBar) {
1505 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1506 bmprc.left = 3;
1507 else
1508 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1509 } else {
1510 bmprc.left = 4;
1511 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1512 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1514 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1515 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1516 bmprc.top = 0;
1517 else
1518 bmprc.top = (lpitem->rect.bottom - lpitem->rect.top -
1519 lpitem->bmpsize.cy) / 2;
1520 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1523 if (!menuBar)
1525 HBITMAP bm;
1526 INT y = rect.top + rect.bottom;
1527 RECT rc = rect;
1528 int checked = FALSE;
1529 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1530 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1531 /* Draw the check mark
1533 * FIXME:
1534 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1536 if( !(menu->dwStyle & MNS_NOCHECK)) {
1537 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1538 lpitem->hUnCheckBit;
1539 if (bm) /* we have a custom bitmap */
1541 HDC hdcMem = CreateCompatibleDC( hdc );
1543 SelectObject( hdcMem, bm );
1544 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1545 check_bitmap_width, check_bitmap_height,
1546 hdcMem, 0, 0, SRCCOPY );
1547 DeleteDC( hdcMem );
1548 checked = TRUE;
1550 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1552 RECT r;
1553 HBITMAP bm = CreateBitmap( check_bitmap_width,
1554 check_bitmap_height, 1, 1, NULL );
1555 HDC hdcMem = CreateCompatibleDC( hdc );
1557 SelectObject( hdcMem, bm );
1558 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1559 DrawFrameControl( hdcMem, &r, DFC_MENU,
1560 (lpitem->fType & MFT_RADIOCHECK) ?
1561 DFCS_MENUBULLET : DFCS_MENUCHECK );
1562 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1563 hdcMem, 0, 0, SRCCOPY );
1564 DeleteDC( hdcMem );
1565 DeleteObject( bm );
1566 checked = TRUE;
1569 if( lpitem->hbmpItem &&
1570 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1571 POINT origorg;
1572 /* some applications make this assumption on the DC's origin */
1573 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1574 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1575 odaction, FALSE);
1576 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1578 /* Draw the popup-menu arrow */
1579 if (lpitem->fType & MF_POPUP)
1580 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1581 arrow_bitmap_height);
1582 rect.left += 4;
1583 if( !(menu->dwStyle & MNS_NOCHECK))
1584 rect.left += check_bitmap_width;
1585 rect.right -= arrow_bitmap_width;
1587 else if( lpitem->hbmpItem)
1588 { /* Draw the bitmap */
1589 POINT origorg;
1591 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1592 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1593 odaction, menuBar);
1594 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1596 /* process text if present */
1597 if (lpitem->text)
1599 register int i;
1600 HFONT hfontOld = 0;
1602 UINT uFormat = (menuBar) ?
1603 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1604 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1606 if( !(menu->dwStyle & MNS_CHECKORBMP))
1607 rect.left += menu->maxBmpSize.cx;
1609 if ( lpitem->fState & MFS_DEFAULT )
1611 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1614 if (menuBar) {
1615 if( lpitem->hbmpItem)
1616 rect.left += lpitem->bmpsize.cx;
1617 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1618 rect.left += menucharsize.cx;
1619 rect.right -= menucharsize.cx;
1622 for (i = 0; lpitem->text[i]; i++)
1623 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1624 break;
1626 if(lpitem->fState & MF_GRAYED)
1628 if (!(lpitem->fState & MF_HILITE) )
1630 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1631 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1632 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1633 --rect.left; --rect.top; --rect.right; --rect.bottom;
1635 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1638 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1640 /* paint the shortcut text */
1641 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1643 if (lpitem->text[i] == '\t')
1645 rect.left = lpitem->xTab;
1646 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1648 else
1650 rect.right = lpitem->xTab;
1651 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1654 if(lpitem->fState & MF_GRAYED)
1656 if (!(lpitem->fState & MF_HILITE) )
1658 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1659 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1660 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1661 --rect.left; --rect.top; --rect.right; --rect.bottom;
1663 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1665 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1668 if (hfontOld)
1669 SelectObject (hdc, hfontOld);
1674 /***********************************************************************
1675 * MENU_DrawPopupMenu
1677 * Paint a popup menu.
1679 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1681 HBRUSH hPrevBrush = 0;
1682 RECT rect;
1684 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1686 GetClientRect( hwnd, &rect );
1688 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1689 && (SelectObject( hdc, get_menu_font(FALSE))))
1691 HPEN hPrevPen;
1693 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1695 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1696 if( hPrevPen )
1698 POPUPMENU *menu;
1699 BOOL flat_menu = FALSE;
1701 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1702 if (flat_menu)
1703 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1704 else
1705 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1707 if( (menu = MENU_GetMenu( hmenu )))
1709 /* draw menu items */
1710 if( menu->nItems)
1712 MENUITEM *item;
1713 UINT u;
1715 item = menu->items;
1716 for( u = menu->nItems; u > 0; u--, item++)
1717 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1718 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1720 /* draw scroll arrows */
1721 if (menu->bScrolling)
1722 MENU_DrawScrollArrows(menu, hdc);
1724 } else
1726 SelectObject( hdc, hPrevBrush );
1731 /***********************************************************************
1732 * MENU_DrawMenuBar
1734 * Paint a menu bar. Returns the height of the menu bar.
1735 * called from [windows/nonclient.c]
1737 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1738 BOOL suppress_draw)
1740 LPPOPUPMENU lppop;
1741 HFONT hfontOld = 0;
1742 HMENU hMenu = GetMenu(hwnd);
1744 lppop = MENU_GetMenu( hMenu );
1745 if (lppop == NULL || lprect == NULL)
1747 return GetSystemMetrics(SM_CYMENU);
1750 if (suppress_draw)
1752 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1754 if (lppop->Height == 0)
1755 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1757 lprect->bottom = lprect->top + lppop->Height;
1759 if (hfontOld) SelectObject( hDC, hfontOld);
1760 return lppop->Height;
1762 else
1763 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1767 /***********************************************************************
1768 * MENU_ShowPopup
1770 * Display a popup menu.
1772 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1773 INT x, INT y, INT xanchor, INT yanchor )
1775 POPUPMENU *menu;
1776 INT width, height;
1777 POINT pt;
1778 HMONITOR monitor;
1779 MONITORINFO info;
1781 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1782 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1784 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1785 if (menu->FocusedItem != NO_SELECTED_ITEM)
1787 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1788 menu->FocusedItem = NO_SELECTED_ITEM;
1791 /* store the owner for DrawItem */
1792 menu->hwndOwner = hwndOwner;
1794 menu->nScrollPos = 0;
1795 MENU_PopupMenuCalcSize( menu, hwndOwner );
1797 /* adjust popup menu pos so that it fits within the desktop */
1799 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1800 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1802 /* FIXME: should use item rect */
1803 pt.x = x;
1804 pt.y = y;
1805 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1806 info.cbSize = sizeof(info);
1807 GetMonitorInfoW( monitor, &info );
1808 if( x + width > info.rcWork.right)
1810 if( xanchor && x >= width - xanchor )
1811 x -= width - xanchor;
1813 if( x + width > info.rcWork.right)
1814 x = info.rcWork.right - width;
1816 if( x < info.rcWork.left ) x = info.rcWork.left;
1818 if( y + height > info.rcWork.bottom)
1820 if( yanchor && y >= height + yanchor )
1821 y -= height + yanchor;
1823 if( y + height > info.rcWork.bottom)
1824 y = info.rcWork.bottom - height;
1826 if( y < info.rcWork.top ) y = info.rcWork.top;
1828 /* NOTE: In Windows, top menu popup is not owned. */
1829 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1830 WS_POPUP, x, y, width, height,
1831 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1832 (LPVOID)hmenu );
1833 if( !menu->hWnd ) return FALSE;
1834 if (!top_popup) top_popup = menu->hWnd;
1836 /* Display the window */
1838 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1839 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1840 UpdateWindow( menu->hWnd );
1841 return TRUE;
1845 /***********************************************************************
1846 * MENU_EnsureMenuItemVisible
1848 static void
1849 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1851 if (lppop->bScrolling)
1853 MENUITEM *item = &lppop->items[wIndex];
1854 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1855 UINT nOldPos = lppop->nScrollPos;
1856 RECT rc;
1857 UINT arrow_bitmap_height;
1858 BITMAP bmp;
1860 GetClientRect(lppop->hWnd, &rc);
1862 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1863 arrow_bitmap_height = bmp.bmHeight;
1865 rc.top += arrow_bitmap_height;
1866 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1868 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1869 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1872 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1873 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1874 MENU_DrawScrollArrows(lppop, hdc);
1876 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1878 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1879 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1880 MENU_DrawScrollArrows(lppop, hdc);
1886 /***********************************************************************
1887 * MENU_SelectItem
1889 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1890 BOOL sendMenuSelect, HMENU topmenu )
1892 LPPOPUPMENU lppop;
1893 HDC hdc;
1895 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1897 lppop = MENU_GetMenu( hmenu );
1898 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1900 if (lppop->FocusedItem == wIndex) return;
1901 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1902 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1903 if (!top_popup) top_popup = lppop->hWnd;
1905 SelectObject( hdc, get_menu_font(FALSE));
1907 /* Clear previous highlighted item */
1908 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1910 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1911 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1912 lppop->Height, !(lppop->wFlags & MF_POPUP),
1913 ODA_SELECT );
1916 /* Highlight new item (if any) */
1917 lppop->FocusedItem = wIndex;
1918 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1920 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1921 lppop->items[wIndex].fState |= MF_HILITE;
1922 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1923 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1924 &lppop->items[wIndex], lppop->Height,
1925 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1927 if (sendMenuSelect)
1929 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1930 SendMessageW( hwndOwner, WM_MENUSELECT,
1931 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1932 ip->fType | ip->fState |
1933 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1936 else if (sendMenuSelect) {
1937 if(topmenu){
1938 int pos;
1939 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1940 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1941 MENUITEM *ip = &ptm->items[pos];
1942 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1943 ip->fType | ip->fState |
1944 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1948 ReleaseDC( lppop->hWnd, hdc );
1952 /***********************************************************************
1953 * MENU_MoveSelection
1955 * Moves currently selected item according to the offset parameter.
1956 * If there is no selection then it should select the last item if
1957 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1959 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1961 INT i;
1962 POPUPMENU *menu;
1964 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1966 menu = MENU_GetMenu( hmenu );
1967 if ((!menu) || (!menu->items)) return;
1969 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1971 if( menu->nItems == 1 ) return; else
1972 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1973 ; i += offset)
1974 if (!(menu->items[i].fType & MF_SEPARATOR))
1976 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1977 return;
1981 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1982 i >= 0 && i < menu->nItems ; i += offset)
1983 if (!(menu->items[i].fType & MF_SEPARATOR))
1985 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1986 return;
1991 /**********************************************************************
1992 * MENU_SetItemData
1994 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1995 * ModifyMenu().
1997 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1998 LPCWSTR str )
2000 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2001 TRACE("flags=%x str=%p\n", flags, str);
2003 if (IS_STRING_ITEM(flags))
2005 LPWSTR prevText = item->text;
2006 if (!str)
2008 flags |= MF_SEPARATOR;
2009 item->text = NULL;
2011 else
2013 LPWSTR text;
2014 /* Item beginning with a backspace is a help item */
2015 if (*str == '\b')
2017 flags |= MF_HELP;
2018 str++;
2020 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2021 return FALSE;
2022 strcpyW( text, str );
2023 item->text = text;
2025 item->hbmpItem = NULL;
2026 HeapFree( GetProcessHeap(), 0, prevText );
2028 else if(( flags & MFT_BITMAP)) {
2029 item->hbmpItem = HBITMAP_32(LOWORD(str));
2030 /* setting bitmap clears text */
2031 HeapFree( GetProcessHeap(), 0, item->text );
2032 item->text = NULL;
2035 if (flags & MF_OWNERDRAW)
2036 item->dwItemData = (DWORD_PTR)str;
2037 else
2038 item->dwItemData = 0;
2040 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2041 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2043 if (flags & MF_POPUP)
2045 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2046 if (menu) menu->wFlags |= MF_POPUP;
2047 else
2049 item->wID = 0;
2050 item->hSubMenu = 0;
2051 item->fType = 0;
2052 item->fState = 0;
2053 return FALSE;
2057 item->wID = id;
2058 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2060 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2061 flags |= MF_POPUP; /* keep popup */
2063 item->fType = flags & TYPE_MASK;
2064 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2065 for ModifyMenu, but Windows accepts it */
2066 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2068 /* Don't call SetRectEmpty here! */
2070 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2071 return TRUE;
2075 /**********************************************************************
2076 * MENU_InsertItem
2078 * Insert (allocate) a new item into a menu.
2080 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2082 MENUITEM *newItems;
2083 POPUPMENU *menu;
2085 if (!(menu = MENU_GetMenu(hMenu)))
2086 return NULL;
2088 /* Find where to insert new item */
2090 if (flags & MF_BYPOSITION) {
2091 if (pos > menu->nItems)
2092 pos = menu->nItems;
2093 } else {
2094 if (!MENU_FindItem( &hMenu, &pos, flags ))
2095 pos = menu->nItems;
2096 else {
2097 if (!(menu = MENU_GetMenu( hMenu )))
2098 return NULL;
2102 /* Create new items array */
2104 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2105 if (!newItems)
2107 WARN("allocation failed\n" );
2108 return NULL;
2110 if (menu->nItems > 0)
2112 /* Copy the old array into the new one */
2113 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2114 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2115 (menu->nItems-pos)*sizeof(MENUITEM) );
2116 HeapFree( GetProcessHeap(), 0, menu->items );
2118 menu->items = newItems;
2119 menu->nItems++;
2120 memset( &newItems[pos], 0, sizeof(*newItems) );
2121 menu->Height = 0; /* force size recalculate */
2122 return &newItems[pos];
2126 /**********************************************************************
2127 * MENU_ParseResource
2129 * Parse a standard menu resource and add items to the menu.
2130 * Return a pointer to the end of the resource.
2132 * NOTE: flags is equivalent to the mtOption field
2134 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2136 WORD flags, id = 0;
2137 LPCSTR str;
2138 BOOL end_flag;
2142 flags = GET_WORD(res);
2143 end_flag = flags & MF_END;
2144 /* Remove MF_END because it has the same value as MF_HILITE */
2145 flags &= ~MF_END;
2146 res += sizeof(WORD);
2147 if (!(flags & MF_POPUP))
2149 id = GET_WORD(res);
2150 res += sizeof(WORD);
2152 str = res;
2153 if (!unicode) res += strlen(str) + 1;
2154 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2155 if (flags & MF_POPUP)
2157 HMENU hSubMenu = CreatePopupMenu();
2158 if (!hSubMenu) return NULL;
2159 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2160 return NULL;
2161 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2162 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2164 else /* Not a popup */
2166 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2167 else AppendMenuW( hMenu, flags, id,
2168 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2170 } while (!end_flag);
2171 return res;
2175 /**********************************************************************
2176 * MENUEX_ParseResource
2178 * Parse an extended menu resource and add items to the menu.
2179 * Return a pointer to the end of the resource.
2181 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2183 WORD resinfo;
2184 do {
2185 MENUITEMINFOW mii;
2187 mii.cbSize = sizeof(mii);
2188 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2189 mii.fType = GET_DWORD(res);
2190 res += sizeof(DWORD);
2191 mii.fState = GET_DWORD(res);
2192 res += sizeof(DWORD);
2193 mii.wID = GET_DWORD(res);
2194 res += sizeof(DWORD);
2195 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2196 res += sizeof(WORD);
2197 /* Align the text on a word boundary. */
2198 res += (~((UINT_PTR)res - 1)) & 1;
2199 mii.dwTypeData = (LPWSTR) res;
2200 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2201 /* Align the following fields on a dword boundary. */
2202 res += (~((UINT_PTR)res - 1)) & 3;
2204 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2205 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2207 if (resinfo & 1) { /* Pop-up? */
2208 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2209 res += sizeof(DWORD);
2210 mii.hSubMenu = CreatePopupMenu();
2211 if (!mii.hSubMenu)
2212 return NULL;
2213 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2214 DestroyMenu(mii.hSubMenu);
2215 return NULL;
2217 mii.fMask |= MIIM_SUBMENU;
2218 mii.fType |= MF_POPUP;
2220 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2222 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2223 mii.wID, mii.fType);
2224 mii.fType |= MF_SEPARATOR;
2226 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2227 } while (!(resinfo & MF_END));
2228 return res;
2232 /***********************************************************************
2233 * MENU_GetSubPopup
2235 * Return the handle of the selected sub-popup menu (if any).
2237 static HMENU MENU_GetSubPopup( HMENU hmenu )
2239 POPUPMENU *menu;
2240 MENUITEM *item;
2242 menu = MENU_GetMenu( hmenu );
2244 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2246 item = &menu->items[menu->FocusedItem];
2247 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2248 return item->hSubMenu;
2249 return 0;
2253 /***********************************************************************
2254 * MENU_HideSubPopups
2256 * Hide the sub-popup menus of this menu.
2258 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2259 BOOL sendMenuSelect )
2261 POPUPMENU *menu = MENU_GetMenu( hmenu );
2263 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2265 if (menu && top_popup)
2267 HMENU hsubmenu;
2268 POPUPMENU *submenu;
2269 MENUITEM *item;
2271 if (menu->FocusedItem != NO_SELECTED_ITEM)
2273 item = &menu->items[menu->FocusedItem];
2274 if (!(item->fType & MF_POPUP) ||
2275 !(item->fState & MF_MOUSESELECT)) return;
2276 item->fState &= ~MF_MOUSESELECT;
2277 hsubmenu = item->hSubMenu;
2278 } else return;
2280 submenu = MENU_GetMenu( hsubmenu );
2281 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2282 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2283 DestroyWindow( submenu->hWnd );
2284 submenu->hWnd = 0;
2289 /***********************************************************************
2290 * MENU_ShowSubPopup
2292 * Display the sub-menu of the selected item of this menu.
2293 * Return the handle of the submenu, or hmenu if no submenu to display.
2295 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2296 BOOL selectFirst, UINT wFlags )
2298 RECT rect;
2299 POPUPMENU *menu;
2300 MENUITEM *item;
2301 HDC hdc;
2303 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2305 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2307 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2309 item = &menu->items[menu->FocusedItem];
2310 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2311 return hmenu;
2313 /* message must be sent before using item,
2314 because nearly everything may be changed by the application ! */
2316 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2317 if (!(wFlags & TPM_NONOTIFY))
2318 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2319 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2321 item = &menu->items[menu->FocusedItem];
2322 rect = item->rect;
2324 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2325 if (!(item->fState & MF_HILITE))
2327 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2328 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2330 SelectObject( hdc, get_menu_font(FALSE));
2332 item->fState |= MF_HILITE;
2333 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2334 ReleaseDC( menu->hWnd, hdc );
2336 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2337 item->rect = rect;
2339 item->fState |= MF_MOUSESELECT;
2341 if (IS_SYSTEM_MENU(menu))
2343 MENU_InitSysMenuPopup(item->hSubMenu,
2344 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2345 GetClassLongW( menu->hWnd, GCL_STYLE));
2347 NC_GetSysPopupPos( menu->hWnd, &rect );
2348 rect.top = rect.bottom;
2349 rect.right = GetSystemMetrics(SM_CXSIZE);
2350 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2352 else
2354 GetWindowRect( menu->hWnd, &rect );
2355 if (menu->wFlags & MF_POPUP)
2357 RECT rc = item->rect;
2359 MENU_AdjustMenuItemRect(menu, &rc);
2361 /* The first item in the popup menu has to be at the
2362 same y position as the focused menu item */
2363 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2364 rect.top += rc.top - MENU_TOP_MARGIN;
2365 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2366 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2367 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2369 else
2371 rect.left += item->rect.left;
2372 rect.top += item->rect.bottom;
2373 rect.right = item->rect.right - item->rect.left;
2374 rect.bottom = item->rect.bottom - item->rect.top;
2378 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2379 rect.left, rect.top, rect.right, rect.bottom );
2380 if (selectFirst)
2381 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2382 return item->hSubMenu;
2387 /**********************************************************************
2388 * MENU_IsMenuActive
2390 HWND MENU_IsMenuActive(void)
2392 return top_popup;
2395 /***********************************************************************
2396 * MENU_PtMenu
2398 * Walks menu chain trying to find a menu pt maps to.
2400 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2402 POPUPMENU *menu = MENU_GetMenu( hMenu );
2403 UINT item = menu->FocusedItem;
2404 HMENU ret;
2406 /* try subpopup first (if any) */
2407 ret = (item != NO_SELECTED_ITEM &&
2408 (menu->items[item].fType & MF_POPUP) &&
2409 (menu->items[item].fState & MF_MOUSESELECT))
2410 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2412 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2414 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2415 if( menu->wFlags & MF_POPUP )
2417 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2419 else if (ht == HTSYSMENU)
2420 ret = get_win_sys_menu( menu->hWnd );
2421 else if (ht == HTMENU)
2422 ret = GetMenu( menu->hWnd );
2424 return ret;
2427 /***********************************************************************
2428 * MENU_ExecFocusedItem
2430 * Execute a menu item (for instance when user pressed Enter).
2431 * Return the wID of the executed item. Otherwise, -1 indicating
2432 * that no menu item was executed, -2 if a popup is shown;
2433 * Have to receive the flags for the TrackPopupMenu options to avoid
2434 * sending unwanted message.
2437 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2439 MENUITEM *item;
2440 POPUPMENU *menu = MENU_GetMenu( hMenu );
2442 TRACE("%p hmenu=%p\n", pmt, hMenu);
2444 if (!menu || !menu->nItems ||
2445 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2447 item = &menu->items[menu->FocusedItem];
2449 TRACE("hMenu %p wID %08x hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2451 if (!(item->fType & MF_POPUP))
2453 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2455 /* If TPM_RETURNCMD is set you return the id, but
2456 do not send a message to the owner */
2457 if(!(wFlags & TPM_RETURNCMD))
2459 if( menu->wFlags & MF_SYSMENU )
2460 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2461 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2462 else
2464 if (menu->dwStyle & MNS_NOTIFYBYPOS)
2465 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2466 (LPARAM)hMenu);
2467 else
2468 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2471 return item->wID;
2474 else
2476 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2477 return -2;
2480 return -1;
2483 /***********************************************************************
2484 * MENU_SwitchTracking
2486 * Helper function for menu navigation routines.
2488 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2490 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2491 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2493 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2495 if( pmt->hTopMenu != hPtMenu &&
2496 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2498 /* both are top level menus (system and menu-bar) */
2499 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2500 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2501 pmt->hTopMenu = hPtMenu;
2503 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2504 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2508 /***********************************************************************
2509 * MENU_ButtonDown
2511 * Return TRUE if we can go on with menu tracking.
2513 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2515 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2517 if (hPtMenu)
2519 UINT id = 0;
2520 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2521 MENUITEM *item;
2523 if( IS_SYSTEM_MENU(ptmenu) )
2524 item = ptmenu->items;
2525 else
2526 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2528 if( item )
2530 if( ptmenu->FocusedItem != id )
2531 MENU_SwitchTracking( pmt, hPtMenu, id );
2533 /* If the popup menu is not already "popped" */
2534 if(!(item->fState & MF_MOUSESELECT ))
2536 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2539 return TRUE;
2541 /* Else the click was on the menu bar, finish the tracking */
2543 return FALSE;
2546 /***********************************************************************
2547 * MENU_ButtonUp
2549 * Return the value of MENU_ExecFocusedItem if
2550 * the selected item was not a popup. Else open the popup.
2551 * A -1 return value indicates that we go on with menu tracking.
2554 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2556 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2558 if (hPtMenu)
2560 UINT id = 0;
2561 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2562 MENUITEM *item;
2564 if( IS_SYSTEM_MENU(ptmenu) )
2565 item = ptmenu->items;
2566 else
2567 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2569 if( item && (ptmenu->FocusedItem == id ))
2571 debug_print_menuitem ("FocusedItem: ", item, "");
2573 if( !(item->fType & MF_POPUP) )
2575 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2576 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2577 return executedMenuId;
2580 /* If we are dealing with the top-level menu */
2581 /* and this is a click on an already "popped" item: */
2582 /* Stop the menu tracking and close the opened submenus */
2583 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2584 return 0;
2586 ptmenu->bTimeToHide = TRUE;
2588 return -1;
2592 /***********************************************************************
2593 * MENU_MouseMove
2595 * Return TRUE if we can go on with menu tracking.
2597 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2599 UINT id = NO_SELECTED_ITEM;
2600 POPUPMENU *ptmenu = NULL;
2602 if( hPtMenu )
2604 ptmenu = MENU_GetMenu( hPtMenu );
2605 if( IS_SYSTEM_MENU(ptmenu) )
2606 id = 0;
2607 else
2608 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2611 if( id == NO_SELECTED_ITEM )
2613 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2614 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2617 else if( ptmenu->FocusedItem != id )
2619 MENU_SwitchTracking( pmt, hPtMenu, id );
2620 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2622 return TRUE;
2626 /***********************************************************************
2627 * MENU_SetCapture
2629 static void MENU_SetCapture( HWND hwnd )
2631 HWND previous = 0;
2633 SERVER_START_REQ( set_capture_window )
2635 req->handle = hwnd;
2636 req->flags = CAPTURE_MENU;
2637 if (!wine_server_call_err( req ))
2639 previous = reply->previous;
2640 hwnd = reply->full_handle;
2643 SERVER_END_REQ;
2645 if (previous && previous != hwnd)
2646 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2650 /***********************************************************************
2651 * MENU_DoNextMenu
2653 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2655 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2657 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2659 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2660 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2662 MDINEXTMENU next_menu;
2663 HMENU hNewMenu;
2664 HWND hNewWnd;
2665 UINT id = 0;
2667 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2668 next_menu.hmenuNext = 0;
2669 next_menu.hwndNext = 0;
2670 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2672 TRACE("%p [%p] -> %p [%p]\n",
2673 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2675 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2677 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2678 hNewWnd = pmt->hOwnerWnd;
2679 if( IS_SYSTEM_MENU(menu) )
2681 /* switch to the menu bar */
2683 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2685 if( vk == VK_LEFT )
2687 menu = MENU_GetMenu( hNewMenu );
2688 id = menu->nItems - 1;
2691 else if (style & WS_SYSMENU )
2693 /* switch to the system menu */
2694 hNewMenu = get_win_sys_menu( hNewWnd );
2696 else return FALSE;
2698 else /* application returned a new menu to switch to */
2700 hNewMenu = next_menu.hmenuNext;
2701 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2703 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2705 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2707 if (style & WS_SYSMENU &&
2708 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2710 /* get the real system menu */
2711 hNewMenu = get_win_sys_menu(hNewWnd);
2713 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2715 /* FIXME: Not sure what to do here;
2716 * perhaps try to track hNewMenu as a popup? */
2718 TRACE(" -- got confused.\n");
2719 return FALSE;
2722 else return FALSE;
2725 if( hNewMenu != pmt->hTopMenu )
2727 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2728 FALSE, 0 );
2729 if( pmt->hCurrentMenu != pmt->hTopMenu )
2730 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2733 if( hNewWnd != pmt->hOwnerWnd )
2735 pmt->hOwnerWnd = hNewWnd;
2736 MENU_SetCapture( pmt->hOwnerWnd );
2739 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2740 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2742 return TRUE;
2744 return FALSE;
2747 /***********************************************************************
2748 * MENU_SuspendPopup
2750 * The idea is not to show the popup if the next input message is
2751 * going to hide it anyway.
2753 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2755 MSG msg;
2757 msg.hwnd = pmt->hOwnerWnd;
2759 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2760 pmt->trackFlags |= TF_SKIPREMOVE;
2762 switch( uMsg )
2764 case WM_KEYDOWN:
2765 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2766 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2768 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2769 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2770 if( msg.message == WM_KEYDOWN &&
2771 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2773 pmt->trackFlags |= TF_SUSPENDPOPUP;
2774 return TRUE;
2777 break;
2780 /* failures go through this */
2781 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2782 return FALSE;
2785 /***********************************************************************
2786 * MENU_KeyEscape
2788 * Handle a VK_ESCAPE key event in a menu.
2790 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2792 BOOL bEndMenu = TRUE;
2794 if (pmt->hCurrentMenu != pmt->hTopMenu)
2796 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2798 if (menu->wFlags & MF_POPUP)
2800 HMENU hmenutmp, hmenuprev;
2802 hmenuprev = hmenutmp = pmt->hTopMenu;
2804 /* close topmost popup */
2805 while (hmenutmp != pmt->hCurrentMenu)
2807 hmenuprev = hmenutmp;
2808 hmenutmp = MENU_GetSubPopup( hmenuprev );
2811 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2812 pmt->hCurrentMenu = hmenuprev;
2813 bEndMenu = FALSE;
2817 return bEndMenu;
2820 /***********************************************************************
2821 * MENU_KeyLeft
2823 * Handle a VK_LEFT key event in a menu.
2825 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2827 POPUPMENU *menu;
2828 HMENU hmenutmp, hmenuprev;
2829 UINT prevcol;
2831 hmenuprev = hmenutmp = pmt->hTopMenu;
2832 menu = MENU_GetMenu( hmenutmp );
2834 /* Try to move 1 column left (if possible) */
2835 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2836 NO_SELECTED_ITEM ) {
2838 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2839 prevcol, TRUE, 0 );
2840 return;
2843 /* close topmost popup */
2844 while (hmenutmp != pmt->hCurrentMenu)
2846 hmenuprev = hmenutmp;
2847 hmenutmp = MENU_GetSubPopup( hmenuprev );
2850 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2851 pmt->hCurrentMenu = hmenuprev;
2853 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2855 /* move menu bar selection if no more popups are left */
2857 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2858 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2860 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2862 /* A sublevel menu was displayed - display the next one
2863 * unless there is another displacement coming up */
2865 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2866 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2867 pmt->hTopMenu, TRUE, wFlags);
2873 /***********************************************************************
2874 * MENU_KeyRight
2876 * Handle a VK_RIGHT key event in a menu.
2878 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2880 HMENU hmenutmp;
2881 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2882 UINT nextcol;
2884 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2885 pmt->hCurrentMenu,
2886 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2887 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2889 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2891 /* If already displaying a popup, try to display sub-popup */
2893 hmenutmp = pmt->hCurrentMenu;
2894 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2896 /* if subpopup was displayed then we are done */
2897 if (hmenutmp != pmt->hCurrentMenu) return;
2900 /* Check to see if there's another column */
2901 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2902 NO_SELECTED_ITEM ) {
2903 TRACE("Going to %d.\n", nextcol );
2904 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2905 nextcol, TRUE, 0 );
2906 return;
2909 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2911 if( pmt->hCurrentMenu != pmt->hTopMenu )
2913 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2914 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2915 } else hmenutmp = 0;
2917 /* try to move to the next item */
2918 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2919 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2921 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2922 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2923 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2924 pmt->hTopMenu, TRUE, wFlags);
2928 /***********************************************************************
2929 * MENU_TrackMenu
2931 * Menu tracking code.
2933 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2934 HWND hwnd, const RECT *lprect )
2936 MSG msg;
2937 POPUPMENU *menu;
2938 BOOL fRemove;
2939 INT executedMenuId = -1;
2940 MTRACKER mt;
2941 BOOL enterIdleSent = FALSE;
2943 mt.trackFlags = 0;
2944 mt.hCurrentMenu = hmenu;
2945 mt.hTopMenu = hmenu;
2946 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2947 mt.pt.x = x;
2948 mt.pt.y = y;
2950 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2951 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2953 fEndMenu = FALSE;
2954 if (!(menu = MENU_GetMenu( hmenu )))
2956 WARN("Invalid menu handle %p\n", hmenu);
2957 SetLastError(ERROR_INVALID_MENU_HANDLE);
2958 return FALSE;
2961 if (wFlags & TPM_BUTTONDOWN)
2963 /* Get the result in order to start the tracking or not */
2964 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2965 fEndMenu = !fRemove;
2968 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2970 MENU_SetCapture( mt.hOwnerWnd );
2972 while (!fEndMenu)
2974 menu = MENU_GetMenu( mt.hCurrentMenu );
2975 if (!menu) /* sometimes happens if I do a window manager close */
2976 break;
2978 /* we have to keep the message in the queue until it's
2979 * clear that menu loop is not over yet. */
2981 for (;;)
2983 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2985 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2986 /* remove the message from the queue */
2987 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2989 else
2991 if (!enterIdleSent)
2993 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2994 enterIdleSent = TRUE;
2995 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2997 WaitMessage();
3001 /* check if EndMenu() tried to cancel us, by posting this message */
3002 if(msg.message == WM_CANCELMODE)
3004 /* we are now out of the loop */
3005 fEndMenu = TRUE;
3007 /* remove the message from the queue */
3008 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3010 /* break out of internal loop, ala ESCAPE */
3011 break;
3014 TranslateMessage( &msg );
3015 mt.pt = msg.pt;
3017 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3018 enterIdleSent=FALSE;
3020 fRemove = FALSE;
3021 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3024 * Use the mouse coordinates in lParam instead of those in the MSG
3025 * struct to properly handle synthetic messages. They are already
3026 * in screen coordinates.
3028 mt.pt.x = (short)LOWORD(msg.lParam);
3029 mt.pt.y = (short)HIWORD(msg.lParam);
3031 /* Find a menu for this mouse event */
3032 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3034 switch(msg.message)
3036 /* no WM_NC... messages in captured state */
3038 case WM_RBUTTONDBLCLK:
3039 case WM_RBUTTONDOWN:
3040 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3041 /* fall through */
3042 case WM_LBUTTONDBLCLK:
3043 case WM_LBUTTONDOWN:
3044 /* If the message belongs to the menu, removes it from the queue */
3045 /* Else, end menu tracking */
3046 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3047 fEndMenu = !fRemove;
3048 break;
3050 case WM_RBUTTONUP:
3051 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3052 /* fall through */
3053 case WM_LBUTTONUP:
3054 /* Check if a menu was selected by the mouse */
3055 if (hmenu)
3057 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3058 TRACE("executedMenuId %d\n", executedMenuId);
3060 /* End the loop if executedMenuId is an item ID */
3061 /* or if the job was done (executedMenuId = 0). */
3062 fEndMenu = fRemove = (executedMenuId != -1);
3064 /* No menu was selected by the mouse */
3065 /* if the function was called by TrackPopupMenu, continue
3066 with the menu tracking. If not, stop it */
3067 else
3068 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3070 break;
3072 case WM_MOUSEMOVE:
3073 /* the selected menu item must be changed every time */
3074 /* the mouse moves. */
3076 if (hmenu)
3077 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3079 } /* switch(msg.message) - mouse */
3081 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3083 fRemove = TRUE; /* Keyboard messages are always removed */
3084 switch(msg.message)
3086 case WM_KEYDOWN:
3087 case WM_SYSKEYDOWN:
3088 switch(msg.wParam)
3090 case VK_MENU:
3091 fEndMenu = TRUE;
3092 break;
3094 case VK_HOME:
3095 case VK_END:
3096 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3097 NO_SELECTED_ITEM, FALSE, 0 );
3098 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3099 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3100 break;
3102 case VK_UP:
3103 case VK_DOWN: /* If on menu bar, pull-down the menu */
3105 menu = MENU_GetMenu( mt.hCurrentMenu );
3106 if (!(menu->wFlags & MF_POPUP))
3107 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3108 else /* otherwise try to move selection */
3109 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3110 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3111 break;
3113 case VK_LEFT:
3114 MENU_KeyLeft( &mt, wFlags );
3115 break;
3117 case VK_RIGHT:
3118 MENU_KeyRight( &mt, wFlags );
3119 break;
3121 case VK_ESCAPE:
3122 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3123 break;
3125 case VK_F1:
3127 HELPINFO hi;
3128 hi.cbSize = sizeof(HELPINFO);
3129 hi.iContextType = HELPINFO_MENUITEM;
3130 if (menu->FocusedItem == NO_SELECTED_ITEM)
3131 hi.iCtrlId = 0;
3132 else
3133 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3134 hi.hItemHandle = hmenu;
3135 hi.dwContextId = menu->dwContextHelpID;
3136 hi.MousePos = msg.pt;
3137 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3138 break;
3141 default:
3142 break;
3144 break; /* WM_KEYDOWN */
3146 case WM_CHAR:
3147 case WM_SYSCHAR:
3149 UINT pos;
3151 if (msg.wParam == '\r' || msg.wParam == ' ')
3153 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3154 fEndMenu = (executedMenuId != -2);
3156 break;
3159 /* Hack to avoid control chars. */
3160 /* We will find a better way real soon... */
3161 if (msg.wParam < 32) break;
3163 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3164 LOWORD(msg.wParam), FALSE );
3165 if (pos == (UINT)-2) fEndMenu = TRUE;
3166 else if (pos == (UINT)-1) MessageBeep(0);
3167 else
3169 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3170 TRUE, 0 );
3171 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3172 fEndMenu = (executedMenuId != -2);
3175 break;
3176 } /* switch(msg.message) - kbd */
3178 else
3180 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3181 DispatchMessageW( &msg );
3182 continue;
3185 if (!fEndMenu) fRemove = TRUE;
3187 /* finally remove message from the queue */
3189 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3190 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3191 else mt.trackFlags &= ~TF_SKIPREMOVE;
3194 MENU_SetCapture(0); /* release the capture */
3196 /* If dropdown is still painted and the close box is clicked on
3197 then the menu will be destroyed as part of the DispatchMessage above.
3198 This will then invalidate the menu handle in mt.hTopMenu. We should
3199 check for this first. */
3200 if( IsMenu( mt.hTopMenu ) )
3202 menu = MENU_GetMenu( mt.hTopMenu );
3204 if( IsWindow( mt.hOwnerWnd ) )
3206 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3208 if (menu && (menu->wFlags & MF_POPUP))
3210 DestroyWindow( menu->hWnd );
3211 menu->hWnd = 0;
3213 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3214 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3217 /* Reset the variable for hiding menu */
3218 if( menu ) menu->bTimeToHide = FALSE;
3221 /* The return value is only used by TrackPopupMenu */
3222 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3223 if (executedMenuId == -1) executedMenuId = 0;
3224 return executedMenuId;
3227 /***********************************************************************
3228 * MENU_InitTracking
3230 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3232 POPUPMENU *menu;
3234 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3236 HideCaret(0);
3238 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3239 if (!(wFlags & TPM_NONOTIFY))
3240 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3242 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3244 if (!(wFlags & TPM_NONOTIFY))
3246 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3247 /* If an app changed/recreated menu bar entries in WM_INITMENU
3248 * menu sizes will be recalculated once the menu created/shown.
3252 /* This makes the menus of applications built with Delphi work.
3253 * It also enables menus to be displayed in more than one window,
3254 * but there are some bugs left that need to be fixed in this case.
3256 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3258 return TRUE;
3260 /***********************************************************************
3261 * MENU_ExitTracking
3263 static BOOL MENU_ExitTracking(HWND hWnd)
3265 TRACE("hwnd=%p\n", hWnd);
3267 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3268 ShowCaret(0);
3269 top_popup = 0;
3270 return TRUE;
3273 /***********************************************************************
3274 * MENU_TrackMouseMenuBar
3276 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3278 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3280 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3281 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3283 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3285 if (IsMenu(hMenu))
3287 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3288 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3289 MENU_ExitTracking(hWnd);
3294 /***********************************************************************
3295 * MENU_TrackKbdMenuBar
3297 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3299 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3301 UINT uItem = NO_SELECTED_ITEM;
3302 HMENU hTrackMenu;
3303 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3305 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3307 /* find window that has a menu */
3309 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3310 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3312 /* check if we have to track a system menu */
3314 hTrackMenu = GetMenu( hwnd );
3315 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3317 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3318 hTrackMenu = get_win_sys_menu( hwnd );
3319 uItem = 0;
3320 wParam |= HTSYSMENU; /* prevent item lookup */
3323 if (!IsMenu( hTrackMenu )) return;
3325 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3327 if( wChar && wChar != ' ' )
3329 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3330 if ( uItem >= (UINT)(-2) )
3332 if( uItem == (UINT)(-1) ) MessageBeep(0);
3333 /* schedule end of menu tracking */
3334 wFlags |= TF_ENDMENU;
3335 goto track_menu;
3339 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3341 if (wParam & HTSYSMENU)
3343 /* prevent sysmenu activation for managed windows on Alt down/up */
3344 if (GetPropA( hwnd, "__wine_x11_managed" ))
3345 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3347 else
3349 if( uItem == NO_SELECTED_ITEM )
3350 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3351 else
3352 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3355 track_menu:
3356 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3357 MENU_ExitTracking( hwnd );
3361 /**********************************************************************
3362 * TrackPopupMenu (USER32.@)
3364 * Like the win32 API, the function return the command ID only if the
3365 * flag TPM_RETURNCMD is on.
3368 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3369 INT nReserved, HWND hWnd, const RECT *lpRect )
3371 BOOL ret = FALSE;
3373 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3374 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3376 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3378 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3379 if (!(wFlags & TPM_NONOTIFY))
3380 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3382 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3383 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3384 MENU_ExitTracking(hWnd);
3386 return ret;
3389 /**********************************************************************
3390 * TrackPopupMenuEx (USER32.@)
3392 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3393 HWND hWnd, LPTPMPARAMS lpTpm )
3395 FIXME("not fully implemented\n" );
3396 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3397 lpTpm ? &lpTpm->rcExclude : NULL );
3400 /***********************************************************************
3401 * PopupMenuWndProc
3403 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3405 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3407 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3409 switch(message)
3411 case WM_CREATE:
3413 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3414 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3415 return 0;
3418 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3419 return MA_NOACTIVATE;
3421 case WM_PAINT:
3423 PAINTSTRUCT ps;
3424 BeginPaint( hwnd, &ps );
3425 MENU_DrawPopupMenu( hwnd, ps.hdc,
3426 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3427 EndPaint( hwnd, &ps );
3428 return 0;
3430 case WM_ERASEBKGND:
3431 return 1;
3433 case WM_DESTROY:
3434 /* zero out global pointer in case resident popup window was destroyed. */
3435 if (hwnd == top_popup) top_popup = 0;
3436 break;
3438 case WM_SHOWWINDOW:
3440 if( wParam )
3442 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3444 else
3445 SetWindowLongPtrW( hwnd, 0, 0 );
3446 break;
3448 case MM_SETMENUHANDLE:
3449 SetWindowLongPtrW( hwnd, 0, wParam );
3450 break;
3452 case MM_GETMENUHANDLE:
3453 return GetWindowLongPtrW( hwnd, 0 );
3455 default:
3456 return DefWindowProcW( hwnd, message, wParam, lParam );
3458 return 0;
3462 /***********************************************************************
3463 * MENU_GetMenuBarHeight
3465 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3467 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3468 INT orgX, INT orgY )
3470 HDC hdc;
3471 RECT rectBar;
3472 LPPOPUPMENU lppop;
3474 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3476 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3478 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3479 SelectObject( hdc, get_menu_font(FALSE));
3480 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3481 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3482 ReleaseDC( hwnd, hdc );
3483 return lppop->Height;
3487 /*******************************************************************
3488 * ChangeMenuA (USER32.@)
3490 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3491 UINT id, UINT flags )
3493 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3494 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3495 id, data );
3496 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3497 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3498 id, data );
3499 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3500 flags & MF_BYPOSITION ? pos : id,
3501 flags & ~MF_REMOVE );
3502 /* Default: MF_INSERT */
3503 return InsertMenuA( hMenu, pos, flags, id, data );
3507 /*******************************************************************
3508 * ChangeMenuW (USER32.@)
3510 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3511 UINT id, UINT flags )
3513 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3514 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3515 id, data );
3516 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3517 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3518 id, data );
3519 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3520 flags & MF_BYPOSITION ? pos : id,
3521 flags & ~MF_REMOVE );
3522 /* Default: MF_INSERT */
3523 return InsertMenuW( hMenu, pos, flags, id, data );
3527 /*******************************************************************
3528 * CheckMenuItem (USER32.@)
3530 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3532 MENUITEM *item;
3533 DWORD ret;
3535 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3536 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3537 ret = item->fState & MF_CHECKED;
3538 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3539 else item->fState &= ~MF_CHECKED;
3540 return ret;
3544 /**********************************************************************
3545 * EnableMenuItem (USER32.@)
3547 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3549 UINT oldflags;
3550 MENUITEM *item;
3551 POPUPMENU *menu;
3553 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3555 /* Get the Popupmenu to access the owner menu */
3556 if (!(menu = MENU_GetMenu(hMenu)))
3557 return (UINT)-1;
3559 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3560 return (UINT)-1;
3562 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3563 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3565 /* If the close item in the system menu change update the close button */
3566 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3568 if (menu->hSysMenuOwner != 0)
3570 RECT rc;
3571 POPUPMENU* parentMenu;
3573 /* Get the parent menu to access*/
3574 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3575 return (UINT)-1;
3577 /* Refresh the frame to reflect the change */
3578 GetWindowRect(parentMenu->hWnd, &rc);
3579 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3580 rc.bottom = 0;
3581 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3585 return oldflags;
3589 /*******************************************************************
3590 * GetMenuStringA (USER32.@)
3592 INT WINAPI GetMenuStringA(
3593 HMENU hMenu, /* [in] menuhandle */
3594 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3595 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3596 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3597 UINT wFlags /* [in] MF_ flags */
3599 MENUITEM *item;
3601 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3602 if (str && nMaxSiz) str[0] = '\0';
3603 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3604 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3605 return 0;
3607 if (!item->text) return 0;
3608 if (!str || !nMaxSiz) return strlenW(item->text);
3609 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3610 str[nMaxSiz-1] = 0;
3611 TRACE("returning '%s'\n", str );
3612 return strlen(str);
3616 /*******************************************************************
3617 * GetMenuStringW (USER32.@)
3619 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3620 LPWSTR str, INT nMaxSiz, UINT wFlags )
3622 MENUITEM *item;
3624 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3625 if (str && nMaxSiz) str[0] = '\0';
3626 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3627 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3628 return 0;
3630 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3631 if( !(item->text)) {
3632 str[0] = 0;
3633 return 0;
3635 lstrcpynW( str, item->text, nMaxSiz );
3636 return strlenW(str);
3640 /**********************************************************************
3641 * HiliteMenuItem (USER32.@)
3643 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3644 UINT wHilite )
3646 LPPOPUPMENU menu;
3647 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3648 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3649 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3650 if (menu->FocusedItem == wItemID) return TRUE;
3651 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3652 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3653 return TRUE;
3657 /**********************************************************************
3658 * GetMenuState (USER32.@)
3660 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3662 MENUITEM *item;
3663 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3664 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3665 debug_print_menuitem (" item: ", item, "");
3666 if (item->fType & MF_POPUP)
3668 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3669 if (!menu) return -1;
3670 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3672 else
3674 /* We used to (from way back then) mask the result to 0xff. */
3675 /* I don't know why and it seems wrong as the documented */
3676 /* return flag MF_SEPARATOR is outside that mask. */
3677 return (item->fType | item->fState);
3682 /**********************************************************************
3683 * GetMenuItemCount (USER32.@)
3685 INT WINAPI GetMenuItemCount( HMENU hMenu )
3687 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3688 if (!menu) return -1;
3689 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3690 return menu->nItems;
3694 /**********************************************************************
3695 * GetMenuItemID (USER32.@)
3697 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3699 MENUITEM * lpmi;
3701 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3702 if (lpmi->fType & MF_POPUP) return -1;
3703 return lpmi->wID;
3708 /*******************************************************************
3709 * InsertMenuW (USER32.@)
3711 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3712 UINT_PTR id, LPCWSTR str )
3714 MENUITEM *item;
3716 if (IS_STRING_ITEM(flags) && str)
3717 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3718 hMenu, pos, flags, id, debugstr_w(str) );
3719 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3720 hMenu, pos, flags, id, str );
3722 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3724 if (!(MENU_SetItemData( item, flags, id, str )))
3726 RemoveMenu( hMenu, pos, flags );
3727 return FALSE;
3730 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3731 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3733 item->hCheckBit = item->hUnCheckBit = 0;
3734 return TRUE;
3738 /*******************************************************************
3739 * InsertMenuA (USER32.@)
3741 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3742 UINT_PTR id, LPCSTR str )
3744 BOOL ret = FALSE;
3746 if (IS_STRING_ITEM(flags) && str)
3748 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3749 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3750 if (newstr)
3752 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3753 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3754 HeapFree( GetProcessHeap(), 0, newstr );
3756 return ret;
3758 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3762 /*******************************************************************
3763 * AppendMenuA (USER32.@)
3765 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3766 UINT_PTR id, LPCSTR data )
3768 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3772 /*******************************************************************
3773 * AppendMenuW (USER32.@)
3775 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3776 UINT_PTR id, LPCWSTR data )
3778 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3782 /**********************************************************************
3783 * RemoveMenu (USER32.@)
3785 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3787 LPPOPUPMENU menu;
3788 MENUITEM *item;
3790 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3791 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3792 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3794 /* Remove item */
3796 MENU_FreeItemData( item );
3798 if (--menu->nItems == 0)
3800 HeapFree( GetProcessHeap(), 0, menu->items );
3801 menu->items = NULL;
3803 else
3805 while(nPos < menu->nItems)
3807 *item = *(item+1);
3808 item++;
3809 nPos++;
3811 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3812 menu->nItems * sizeof(MENUITEM) );
3814 return TRUE;
3818 /**********************************************************************
3819 * DeleteMenu (USER32.@)
3821 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3823 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3824 if (!item) return FALSE;
3825 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3826 /* nPos is now the position of the item */
3827 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3828 return TRUE;
3832 /*******************************************************************
3833 * ModifyMenuW (USER32.@)
3835 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3836 UINT_PTR id, LPCWSTR str )
3838 MENUITEM *item;
3840 if (IS_STRING_ITEM(flags))
3841 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3842 else
3843 TRACE("%p %d %04x %04x %p\n", hMenu, pos, flags, id, str );
3845 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3846 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3847 return MENU_SetItemData( item, flags, id, str );
3851 /*******************************************************************
3852 * ModifyMenuA (USER32.@)
3854 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3855 UINT_PTR id, LPCSTR str )
3857 BOOL ret = FALSE;
3859 if (IS_STRING_ITEM(flags) && str)
3861 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3862 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3863 if (newstr)
3865 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3866 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3867 HeapFree( GetProcessHeap(), 0, newstr );
3869 return ret;
3871 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3875 /**********************************************************************
3876 * CreatePopupMenu (USER32.@)
3878 HMENU WINAPI CreatePopupMenu(void)
3880 HMENU hmenu;
3881 POPUPMENU *menu;
3883 if (!(hmenu = CreateMenu())) return 0;
3884 menu = MENU_GetMenu( hmenu );
3885 menu->wFlags |= MF_POPUP;
3886 menu->bTimeToHide = FALSE;
3887 return hmenu;
3891 /**********************************************************************
3892 * GetMenuCheckMarkDimensions (USER.417)
3893 * GetMenuCheckMarkDimensions (USER32.@)
3895 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3897 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3901 /**********************************************************************
3902 * SetMenuItemBitmaps (USER32.@)
3904 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3905 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3907 MENUITEM *item;
3908 TRACE("(%p, %04x, %04x, %p, %p)\n",
3909 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3910 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3912 if (!hNewCheck && !hNewUnCheck)
3914 item->fState &= ~MF_USECHECKBITMAPS;
3916 else /* Install new bitmaps */
3918 item->hCheckBit = hNewCheck;
3919 item->hUnCheckBit = hNewUnCheck;
3920 item->fState |= MF_USECHECKBITMAPS;
3922 return TRUE;
3926 /**********************************************************************
3927 * CreateMenu (USER32.@)
3929 HMENU WINAPI CreateMenu(void)
3931 HMENU hMenu;
3932 LPPOPUPMENU menu;
3933 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3934 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3936 ZeroMemory(menu, sizeof(POPUPMENU));
3937 menu->wMagic = MENU_MAGIC;
3938 menu->FocusedItem = NO_SELECTED_ITEM;
3939 menu->bTimeToHide = FALSE;
3941 TRACE("return %p\n", hMenu );
3943 return hMenu;
3947 /**********************************************************************
3948 * DestroyMenu (USER32.@)
3950 BOOL WINAPI DestroyMenu( HMENU hMenu )
3952 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3954 TRACE("(%p)\n", hMenu);
3957 if (!lppop) return FALSE;
3959 lppop->wMagic = 0; /* Mark it as destroyed */
3961 /* DestroyMenu should not destroy system menu popup owner */
3962 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3964 DestroyWindow( lppop->hWnd );
3965 lppop->hWnd = 0;
3968 if (lppop->items) /* recursively destroy submenus */
3970 int i;
3971 MENUITEM *item = lppop->items;
3972 for (i = lppop->nItems; i > 0; i--, item++)
3974 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3975 MENU_FreeItemData( item );
3977 HeapFree( GetProcessHeap(), 0, lppop->items );
3979 USER_HEAP_FREE( hMenu );
3980 return TRUE;
3984 /**********************************************************************
3985 * GetSystemMenu (USER32.@)
3987 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3989 WND *wndPtr = WIN_GetPtr( hWnd );
3990 HMENU retvalue = 0;
3992 if (wndPtr == WND_DESKTOP) return 0;
3993 if (wndPtr == WND_OTHER_PROCESS)
3995 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3997 else if (wndPtr)
3999 if (wndPtr->hSysMenu && bRevert)
4001 DestroyMenu(wndPtr->hSysMenu);
4002 wndPtr->hSysMenu = 0;
4005 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4006 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4008 if( wndPtr->hSysMenu )
4010 POPUPMENU *menu;
4011 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4013 /* Store the dummy sysmenu handle to facilitate the refresh */
4014 /* of the close button if the SC_CLOSE item change */
4015 menu = MENU_GetMenu(retvalue);
4016 if ( menu )
4017 menu->hSysMenuOwner = wndPtr->hSysMenu;
4019 WIN_ReleasePtr( wndPtr );
4021 return bRevert ? 0 : retvalue;
4025 /*******************************************************************
4026 * SetSystemMenu (USER32.@)
4028 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4030 WND *wndPtr = WIN_GetPtr( hwnd );
4032 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4034 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4035 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4036 WIN_ReleasePtr( wndPtr );
4037 return TRUE;
4039 return FALSE;
4043 /**********************************************************************
4044 * GetMenu (USER32.@)
4046 HMENU WINAPI GetMenu( HWND hWnd )
4048 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4049 TRACE("for %p returning %p\n", hWnd, retvalue);
4050 return retvalue;
4053 /**********************************************************************
4054 * GetMenuBarInfo (USER32.@)
4056 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4058 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4059 return FALSE;
4062 /**********************************************************************
4063 * MENU_SetMenu
4065 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4066 * SetWindowPos call that would result if SetMenu were called directly.
4068 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4070 TRACE("(%p, %p);\n", hWnd, hMenu);
4072 if (hMenu && !IsMenu(hMenu))
4074 WARN("hMenu %p is not a menu handle\n", hMenu);
4075 return FALSE;
4077 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4078 return FALSE;
4080 hWnd = WIN_GetFullHandle( hWnd );
4081 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4083 if (hMenu != 0)
4085 LPPOPUPMENU lpmenu;
4087 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4089 lpmenu->hWnd = hWnd;
4090 lpmenu->Height = 0; /* Make sure we recalculate the size */
4092 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4093 return TRUE;
4097 /**********************************************************************
4098 * SetMenu (USER32.@)
4100 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4102 if(!MENU_SetMenu(hWnd, hMenu))
4103 return FALSE;
4105 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4106 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4107 return TRUE;
4111 /**********************************************************************
4112 * GetSubMenu (USER32.@)
4114 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4116 MENUITEM * lpmi;
4118 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4119 if (!(lpmi->fType & MF_POPUP)) return 0;
4120 return lpmi->hSubMenu;
4124 /**********************************************************************
4125 * DrawMenuBar (USER32.@)
4127 BOOL WINAPI DrawMenuBar( HWND hWnd )
4129 LPPOPUPMENU lppop;
4130 HMENU hMenu = GetMenu(hWnd);
4132 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4133 return FALSE;
4134 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4136 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4137 lppop->hwndOwner = hWnd;
4138 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4139 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4140 return TRUE;
4143 /***********************************************************************
4144 * DrawMenuBarTemp (USER32.@)
4146 * UNDOCUMENTED !!
4148 * called by W98SE desk.cpl Control Panel Applet
4150 * Not 100% sure about the param names, but close.
4152 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4154 LPPOPUPMENU lppop;
4155 UINT i,retvalue;
4156 HFONT hfontOld = 0;
4157 BOOL flat_menu = FALSE;
4159 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4161 if (!hMenu)
4162 hMenu = GetMenu(hwnd);
4164 if (!hFont)
4165 hFont = get_menu_font(FALSE);
4167 lppop = MENU_GetMenu( hMenu );
4168 if (lppop == NULL || lprect == NULL)
4170 retvalue = GetSystemMetrics(SM_CYMENU);
4171 goto END;
4174 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4176 hfontOld = SelectObject( hDC, hFont);
4178 if (lppop->Height == 0)
4179 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4181 lprect->bottom = lprect->top + lppop->Height;
4183 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4185 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4186 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4187 LineTo( hDC, lprect->right, lprect->bottom );
4189 if (lppop->nItems == 0)
4191 retvalue = GetSystemMetrics(SM_CYMENU);
4192 goto END;
4195 for (i = 0; i < lppop->nItems; i++)
4197 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4198 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4200 retvalue = lppop->Height;
4202 END:
4203 if (hfontOld) SelectObject (hDC, hfontOld);
4204 return retvalue;
4207 /***********************************************************************
4208 * EndMenu (USER.187)
4209 * EndMenu (USER32.@)
4211 void WINAPI EndMenu(void)
4213 /* if we are in the menu code, and it is active */
4214 if (!fEndMenu && top_popup)
4216 /* terminate the menu handling code */
4217 fEndMenu = TRUE;
4219 /* needs to be posted to wakeup the internal menu handler */
4220 /* which will now terminate the menu, in the event that */
4221 /* the main window was minimized, or lost focus, so we */
4222 /* don't end up with an orphaned menu */
4223 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4228 /***********************************************************************
4229 * LookupMenuHandle (USER.217)
4231 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4233 HMENU hmenu32 = HMENU_32(hmenu);
4234 UINT id32 = id;
4235 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4236 else return HMENU_16(hmenu32);
4240 /**********************************************************************
4241 * LoadMenu (USER.150)
4243 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4245 HRSRC16 hRsrc;
4246 HGLOBAL16 handle;
4247 HMENU16 hMenu;
4249 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4250 if (!name) return 0;
4252 instance = GetExePtr( instance );
4253 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4254 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4255 hMenu = LoadMenuIndirect16(LockResource16(handle));
4256 FreeResource16( handle );
4257 return hMenu;
4261 /*****************************************************************
4262 * LoadMenuA (USER32.@)
4264 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4266 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4267 if (!hrsrc) return 0;
4268 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4272 /*****************************************************************
4273 * LoadMenuW (USER32.@)
4275 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4277 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4278 if (!hrsrc) return 0;
4279 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4283 /**********************************************************************
4284 * LoadMenuIndirect (USER.220)
4286 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4288 HMENU hMenu;
4289 WORD version, offset;
4290 LPCSTR p = (LPCSTR)template;
4292 TRACE("(%p)\n", template );
4293 version = GET_WORD(p);
4294 p += sizeof(WORD);
4295 if (version)
4297 WARN("version must be 0 for Win16\n" );
4298 return 0;
4300 offset = GET_WORD(p);
4301 p += sizeof(WORD) + offset;
4302 if (!(hMenu = CreateMenu())) return 0;
4303 if (!MENU_ParseResource( p, hMenu, FALSE ))
4305 DestroyMenu( hMenu );
4306 return 0;
4308 return HMENU_16(hMenu);
4312 /**********************************************************************
4313 * LoadMenuIndirectW (USER32.@)
4315 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4317 HMENU hMenu;
4318 WORD version, offset;
4319 LPCSTR p = (LPCSTR)template;
4321 version = GET_WORD(p);
4322 p += sizeof(WORD);
4323 TRACE("%p, ver %d\n", template, version );
4324 switch (version)
4326 case 0: /* standard format is version of 0 */
4327 offset = GET_WORD(p);
4328 p += sizeof(WORD) + offset;
4329 if (!(hMenu = CreateMenu())) return 0;
4330 if (!MENU_ParseResource( p, hMenu, TRUE ))
4332 DestroyMenu( hMenu );
4333 return 0;
4335 return hMenu;
4336 case 1: /* extended format is version of 1 */
4337 offset = GET_WORD(p);
4338 p += sizeof(WORD) + offset;
4339 if (!(hMenu = CreateMenu())) return 0;
4340 if (!MENUEX_ParseResource( p, hMenu))
4342 DestroyMenu( hMenu );
4343 return 0;
4345 return hMenu;
4346 default:
4347 ERR("version %d not supported.\n", version);
4348 return 0;
4353 /**********************************************************************
4354 * LoadMenuIndirectA (USER32.@)
4356 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4358 return LoadMenuIndirectW( template );
4362 /**********************************************************************
4363 * IsMenu (USER32.@)
4365 BOOL WINAPI IsMenu(HMENU hmenu)
4367 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4369 if (!menu)
4371 SetLastError(ERROR_INVALID_MENU_HANDLE);
4372 return FALSE;
4374 return TRUE;
4377 /**********************************************************************
4378 * check_MENUITEMINFO_size
4380 static BOOL check_MENUITEMINFO_size(LPMENUITEMINFOW lpmii)
4382 if (lpmii->cbSize == sizeof(MENUITEMINFOW))
4383 return TRUE;
4385 * an old version of MENUITEMINFO lacked the hbmpItem field.
4387 if (lpmii->cbSize == (sizeof(MENUITEMINFOW) - sizeof(HBITMAP)))
4388 return TRUE;
4390 return FALSE;
4393 /**********************************************************************
4394 * GetMenuItemInfo_common
4397 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4398 LPMENUITEMINFOW lpmii, BOOL unicode)
4400 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4402 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4404 if (!menu)
4405 return FALSE;
4407 /* check for a valid structure */
4408 if (!check_MENUITEMINFO_size(lpmii))
4410 ERR("Invalid structure size: %i\n", lpmii->cbSize);
4411 SetLastError(ERROR_BAD_ARGUMENTS);
4412 return FALSE;
4415 if( lpmii->fMask & MIIM_TYPE) {
4416 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4417 WARN("invalid combination of fMask bits used\n");
4418 /* this does not happen on Win9x/ME */
4419 SetLastError( ERROR_INVALID_PARAMETER);
4420 return FALSE;
4422 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4423 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4424 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4425 if( lpmii->fType & MFT_BITMAP) {
4426 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4427 lpmii->cch = 0;
4428 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4429 /* this does not happen on Win9x/ME */
4430 lpmii->dwTypeData = 0;
4431 lpmii->cch = 0;
4435 /* copy the text string */
4436 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4437 if( !menu->text ) {
4438 if(lpmii->dwTypeData && lpmii->cch) {
4439 lpmii->cch = 0;
4440 if( unicode)
4441 *((WCHAR *)lpmii->dwTypeData) = 0;
4442 else
4443 *((CHAR *)lpmii->dwTypeData) = 0;
4445 } else {
4446 int len;
4447 if (unicode)
4449 len = strlenW(menu->text);
4450 if(lpmii->dwTypeData && lpmii->cch)
4451 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4453 else
4455 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4456 0, NULL, NULL ) - 1;
4457 if(lpmii->dwTypeData && lpmii->cch)
4458 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4459 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4460 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4462 /* if we've copied a substring we return its length */
4463 if(lpmii->dwTypeData && lpmii->cch)
4464 if (lpmii->cch <= len + 1)
4465 lpmii->cch--;
4466 else
4467 lpmii->cch = len;
4468 else {
4469 /* return length of string */
4470 /* not on Win9x/ME if fType & MFT_BITMAP */
4471 lpmii->cch = len;
4476 if (lpmii->fMask & MIIM_FTYPE)
4477 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4479 if (lpmii->fMask & MIIM_BITMAP)
4480 lpmii->hbmpItem = menu->hbmpItem;
4482 if (lpmii->fMask & MIIM_STATE)
4483 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4485 if (lpmii->fMask & MIIM_ID)
4486 lpmii->wID = menu->wID;
4488 if (lpmii->fMask & MIIM_SUBMENU)
4489 lpmii->hSubMenu = menu->hSubMenu;
4490 else {
4491 /* hSubMenu is always cleared
4492 * (not on Win9x/ME ) */
4493 lpmii->hSubMenu = 0;
4496 if (lpmii->fMask & MIIM_CHECKMARKS) {
4497 lpmii->hbmpChecked = menu->hCheckBit;
4498 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4500 if (lpmii->fMask & MIIM_DATA)
4501 lpmii->dwItemData = menu->dwItemData;
4503 return TRUE;
4506 /**********************************************************************
4507 * GetMenuItemInfoA (USER32.@)
4509 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4510 LPMENUITEMINFOA lpmii)
4512 BOOL ret;
4513 MENUITEMINFOA mii;
4514 if( lpmii->cbSize != sizeof( mii) &&
4515 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4516 SetLastError( ERROR_INVALID_PARAMETER);
4517 return FALSE;
4519 memcpy( &mii, lpmii, lpmii->cbSize);
4520 mii.cbSize = sizeof( mii);
4521 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4522 (LPMENUITEMINFOW)&mii, FALSE);
4523 mii.cbSize = lpmii->cbSize;
4524 memcpy( lpmii, &mii, mii.cbSize);
4525 return ret;
4528 /**********************************************************************
4529 * GetMenuItemInfoW (USER32.@)
4531 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4532 LPMENUITEMINFOW lpmii)
4534 BOOL ret;
4535 MENUITEMINFOW mii;
4536 if( lpmii->cbSize != sizeof( mii) &&
4537 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4538 SetLastError( ERROR_INVALID_PARAMETER);
4539 return FALSE;
4541 memcpy( &mii, lpmii, lpmii->cbSize);
4542 mii.cbSize = sizeof( mii);
4543 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4544 mii.cbSize = lpmii->cbSize;
4545 memcpy( lpmii, &mii, mii.cbSize);
4546 return ret;
4550 /* set a menu item text from a ASCII or Unicode string */
4551 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4553 if (!text)
4554 menu->text = NULL;
4555 else if (unicode)
4557 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4558 strcpyW( menu->text, text );
4560 else
4562 LPCSTR str = (LPCSTR)text;
4563 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4564 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4565 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4570 /**********************************************************************
4571 * SetMenuItemInfo_common
4574 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4575 const MENUITEMINFOW *lpmii,
4576 BOOL unicode)
4578 if (!menu) return FALSE;
4580 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4582 if (lpmii->fMask & ~(MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE
4583 | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU |
4584 MIIM_TYPE))
4586 FIXME("Invalid or Unhandled fMask(%x)\n",lpmii->fMask);
4587 return FALSE;
4590 if (lpmii->fMask & MIIM_TYPE ) {
4591 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4592 WARN("invalid combination of fMask bits used\n");
4593 /* this does not happen on Win9x/ME */
4594 SetLastError( ERROR_INVALID_PARAMETER);
4595 return FALSE;
4598 /* Remove the old type bits and replace them with the new ones */
4599 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4600 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4602 if (IS_STRING_ITEM(menu->fType)) {
4603 HeapFree(GetProcessHeap(), 0, menu->text);
4604 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4605 } else if( (menu->fType) & MFT_BITMAP)
4606 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4609 if (lpmii->fMask & MIIM_FTYPE ) {
4610 if(( lpmii->fType & MFT_BITMAP)) {
4611 SetLastError( ERROR_INVALID_PARAMETER);
4612 return FALSE;
4614 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4615 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4617 if (lpmii->fMask & MIIM_STRING ) {
4618 /* free the string when used */
4619 HeapFree(GetProcessHeap(), 0, menu->text);
4620 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4623 if (lpmii->fMask & MIIM_STATE)
4625 /* Other menu items having MFS_DEFAULT are not converted
4626 to normal items */
4627 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4630 if (lpmii->fMask & MIIM_ID)
4631 menu->wID = lpmii->wID;
4633 if (lpmii->fMask & MIIM_SUBMENU) {
4634 menu->hSubMenu = lpmii->hSubMenu;
4635 if (menu->hSubMenu) {
4636 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4637 if (subMenu) {
4638 subMenu->wFlags |= MF_POPUP;
4639 menu->fType |= MF_POPUP;
4641 else {
4642 SetLastError( ERROR_INVALID_PARAMETER);
4643 return FALSE;
4646 else
4647 menu->fType &= ~MF_POPUP;
4650 if (lpmii->fMask & MIIM_CHECKMARKS)
4652 menu->hCheckBit = lpmii->hbmpChecked;
4653 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4655 if (lpmii->fMask & MIIM_DATA)
4656 menu->dwItemData = lpmii->dwItemData;
4658 if (lpmii->fMask & MIIM_BITMAP)
4659 menu->hbmpItem = lpmii->hbmpItem;
4661 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4662 menu->fType |= MFT_SEPARATOR;
4664 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4665 return TRUE;
4668 /**********************************************************************
4669 * SetMenuItemInfoA (USER32.@)
4671 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4672 const MENUITEMINFOA *lpmii)
4674 MENUITEMINFOA mii;
4675 if( lpmii->cbSize != sizeof( mii) &&
4676 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4677 SetLastError( ERROR_INVALID_PARAMETER);
4678 return FALSE;
4680 memcpy( &mii, lpmii, lpmii->cbSize);
4681 if( lpmii->cbSize != sizeof( mii)) {
4682 mii.cbSize = sizeof( mii);
4683 mii.hbmpItem = NULL;
4685 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4686 (const MENUITEMINFOW *)&mii, FALSE);
4689 /**********************************************************************
4690 * SetMenuItemInfoW (USER32.@)
4692 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4693 const MENUITEMINFOW *lpmii)
4695 MENUITEMINFOW mii;
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 = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4808 MENUITEMINFOA mii;
4809 if( lpmii->cbSize != sizeof( mii) &&
4810 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4811 SetLastError( ERROR_INVALID_PARAMETER);
4812 return FALSE;
4814 memcpy( &mii, lpmii, lpmii->cbSize);
4815 if( lpmii->cbSize != sizeof( mii)) {
4816 mii.cbSize = sizeof( mii);
4817 mii.hbmpItem = NULL;
4819 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4823 /**********************************************************************
4824 * InsertMenuItemW (USER32.@)
4826 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4827 const MENUITEMINFOW *lpmii)
4829 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4830 MENUITEMINFOW mii;
4831 if( lpmii->cbSize != sizeof( mii) &&
4832 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4833 SetLastError( ERROR_INVALID_PARAMETER);
4834 return FALSE;
4836 memcpy( &mii, lpmii, lpmii->cbSize);
4837 if( lpmii->cbSize != sizeof( mii)) {
4838 mii.cbSize = sizeof( mii);
4839 mii.hbmpItem = NULL;
4841 return SetMenuItemInfo_common(item, &mii, TRUE);
4844 /**********************************************************************
4845 * CheckMenuRadioItem (USER32.@)
4848 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4849 UINT first, UINT last, UINT check,
4850 UINT bypos)
4852 MENUITEM *mifirst, *milast, *micheck;
4853 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4855 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4857 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4858 milast = MENU_FindItem (&mlast, &last, bypos);
4859 micheck = MENU_FindItem (&mcheck, &check, bypos);
4861 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4862 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4863 micheck > milast || micheck < mifirst)
4864 return FALSE;
4866 while (mifirst <= milast)
4868 if (mifirst == micheck)
4870 mifirst->fType |= MFT_RADIOCHECK;
4871 mifirst->fState |= MFS_CHECKED;
4872 } else {
4873 mifirst->fType &= ~MFT_RADIOCHECK;
4874 mifirst->fState &= ~MFS_CHECKED;
4876 mifirst++;
4879 return TRUE;
4883 /**********************************************************************
4884 * GetMenuItemRect (USER32.@)
4886 * ATTENTION: Here, the returned values in rect are the screen
4887 * coordinates of the item just like if the menu was
4888 * always on the upper left side of the application.
4891 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4892 LPRECT rect)
4894 POPUPMENU *itemMenu;
4895 MENUITEM *item;
4896 HWND referenceHwnd;
4898 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4900 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4901 referenceHwnd = hwnd;
4903 if(!hwnd)
4905 itemMenu = MENU_GetMenu(hMenu);
4906 if (itemMenu == NULL)
4907 return FALSE;
4909 if(itemMenu->hWnd == 0)
4910 return FALSE;
4911 referenceHwnd = itemMenu->hWnd;
4914 if ((rect == NULL) || (item == NULL))
4915 return FALSE;
4917 *rect = item->rect;
4919 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4921 return TRUE;
4925 /**********************************************************************
4926 * SetMenuInfo (USER32.@)
4928 * FIXME
4929 * MIM_APPLYTOSUBMENUS
4930 * actually use the items to draw the menu
4932 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4934 POPUPMENU *menu;
4936 TRACE("(%p %p)\n", hMenu, lpmi);
4938 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4941 if (lpmi->fMask & MIM_BACKGROUND)
4942 menu->hbrBack = lpmi->hbrBack;
4944 if (lpmi->fMask & MIM_HELPID)
4945 menu->dwContextHelpID = lpmi->dwContextHelpID;
4947 if (lpmi->fMask & MIM_MAXHEIGHT)
4948 menu->cyMax = lpmi->cyMax;
4950 if (lpmi->fMask & MIM_MENUDATA)
4951 menu->dwMenuData = lpmi->dwMenuData;
4953 if (lpmi->fMask & MIM_STYLE)
4955 menu->dwStyle = lpmi->dwStyle;
4956 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4957 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4958 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4959 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
4962 return TRUE;
4964 return FALSE;
4967 /**********************************************************************
4968 * GetMenuInfo (USER32.@)
4970 * NOTES
4971 * win98/NT5.0
4974 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4975 { POPUPMENU *menu;
4977 TRACE("(%p %p)\n", hMenu, lpmi);
4979 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4982 if (lpmi->fMask & MIM_BACKGROUND)
4983 lpmi->hbrBack = menu->hbrBack;
4985 if (lpmi->fMask & MIM_HELPID)
4986 lpmi->dwContextHelpID = menu->dwContextHelpID;
4988 if (lpmi->fMask & MIM_MAXHEIGHT)
4989 lpmi->cyMax = menu->cyMax;
4991 if (lpmi->fMask & MIM_MENUDATA)
4992 lpmi->dwMenuData = menu->dwMenuData;
4994 if (lpmi->fMask & MIM_STYLE)
4995 lpmi->dwStyle = menu->dwStyle;
4997 return TRUE;
4999 return FALSE;
5003 /**********************************************************************
5004 * SetMenuContextHelpId (USER32.@)
5006 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5008 LPPOPUPMENU menu;
5010 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5012 if ((menu = MENU_GetMenu(hMenu)))
5014 menu->dwContextHelpID = dwContextHelpID;
5015 return TRUE;
5017 return FALSE;
5021 /**********************************************************************
5022 * GetMenuContextHelpId (USER32.@)
5024 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5026 LPPOPUPMENU menu;
5028 TRACE("(%p)\n", hMenu);
5030 if ((menu = MENU_GetMenu(hMenu)))
5032 return menu->dwContextHelpID;
5034 return 0;
5037 /**********************************************************************
5038 * MenuItemFromPoint (USER32.@)
5040 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5042 POPUPMENU *menu = MENU_GetMenu(hMenu);
5043 UINT pos;
5045 /*FIXME: Do we have to handle hWnd here? */
5046 if (!menu) return -1;
5047 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5048 return pos;
5052 /**********************************************************************
5053 * translate_accelerator
5055 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5056 BYTE fVirt, WORD key, WORD cmd )
5058 INT mask = 0;
5059 UINT mesg = 0;
5061 if (wParam != key) return FALSE;
5063 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5064 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5065 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5067 if (message == WM_CHAR || message == WM_SYSCHAR)
5069 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5071 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
5072 goto found;
5075 else
5077 if(fVirt & FVIRTKEY)
5079 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5080 wParam, 0xff & HIWORD(lParam));
5082 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5083 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5085 else
5087 if (!(lParam & 0x01000000)) /* no special_key */
5089 if ((fVirt & FALT) && (lParam & 0x20000000))
5090 { /* ^^ ALT pressed */
5091 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5092 goto found;
5097 return FALSE;
5099 found:
5100 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5101 mesg = 1;
5102 else
5104 HMENU hMenu, hSubMenu, hSysMenu;
5105 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5107 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5108 hSysMenu = get_win_sys_menu( hWnd );
5110 /* find menu item and ask application to initialize it */
5111 /* 1. in the system menu */
5112 hSubMenu = hSysMenu;
5113 nPos = cmd;
5114 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5116 if (GetCapture())
5117 mesg = 2;
5118 if (!IsWindowEnabled(hWnd))
5119 mesg = 3;
5120 else
5122 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5123 if(hSubMenu != hSysMenu)
5125 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5126 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5127 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5129 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5132 else /* 2. in the window's menu */
5134 hSubMenu = hMenu;
5135 nPos = cmd;
5136 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5138 if (GetCapture())
5139 mesg = 2;
5140 if (!IsWindowEnabled(hWnd))
5141 mesg = 3;
5142 else
5144 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5145 if(hSubMenu != hMenu)
5147 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5148 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5149 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5151 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5156 if (mesg == 0)
5158 if (uSysStat != (UINT)-1)
5160 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5161 mesg=4;
5162 else
5163 mesg=WM_SYSCOMMAND;
5165 else
5167 if (uStat != (UINT)-1)
5169 if (IsIconic(hWnd))
5170 mesg=5;
5171 else
5173 if (uStat & (MF_DISABLED|MF_GRAYED))
5174 mesg=6;
5175 else
5176 mesg=WM_COMMAND;
5179 else
5180 mesg=WM_COMMAND;
5185 if( mesg==WM_COMMAND )
5187 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5188 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5190 else if( mesg==WM_SYSCOMMAND )
5192 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5193 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5195 else
5197 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5198 * #0: unknown (please report!)
5199 * #1: for WM_KEYUP,WM_SYSKEYUP
5200 * #2: mouse is captured
5201 * #3: window is disabled
5202 * #4: it's a disabled system menu option
5203 * #5: it's a menu option, but window is iconic
5204 * #6: it's a menu option, but disabled
5206 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5207 if(mesg==0)
5208 ERR_(accel)(" unknown reason - please report!\n");
5210 return TRUE;
5213 /**********************************************************************
5214 * TranslateAcceleratorA (USER32.@)
5215 * TranslateAccelerator (USER32.@)
5217 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5219 /* YES, Accel16! */
5220 LPACCEL16 lpAccelTbl;
5221 int i;
5222 WPARAM wParam;
5224 if (!hWnd || !msg) return 0;
5226 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5228 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5229 return 0;
5232 wParam = msg->wParam;
5234 switch (msg->message)
5236 case WM_KEYDOWN:
5237 case WM_SYSKEYDOWN:
5238 break;
5240 case WM_CHAR:
5241 case WM_SYSCHAR:
5243 char ch = LOWORD(wParam);
5244 WCHAR wch;
5245 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5246 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5248 break;
5250 default:
5251 return 0;
5254 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5255 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5256 i = 0;
5259 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5260 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5261 return 1;
5262 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5264 return 0;
5267 /**********************************************************************
5268 * TranslateAcceleratorW (USER32.@)
5270 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5272 /* YES, Accel16! */
5273 LPACCEL16 lpAccelTbl;
5274 int i;
5276 if (!hWnd || !msg) return 0;
5278 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5280 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5281 return 0;
5284 switch (msg->message)
5286 case WM_KEYDOWN:
5287 case WM_SYSKEYDOWN:
5288 case WM_CHAR:
5289 case WM_SYSCHAR:
5290 break;
5292 default:
5293 return 0;
5296 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5297 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5298 i = 0;
5301 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5302 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5303 return 1;
5304 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5306 return 0;