Assorted spelling fixes.
[wine.git] / dlls / user / menu.c
bloba1a1c040eac7b18130dd950e3a516d3a3c0ae73b
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
40 * - MNS_NOTIFYBYPOS
43 #include "config.h"
44 #include "wine/port.h"
46 #include <stdarg.h>
47 #include <string.h>
49 #include "windef.h"
50 #include "winbase.h"
51 #include "wingdi.h"
52 #include "winnls.h"
53 #include "wine/winbase16.h"
54 #include "wine/winuser16.h"
55 #include "wownt32.h"
56 #include "wine/server.h"
57 #include "wine/unicode.h"
58 #include "win.h"
59 #include "controls.h"
60 #include "user_private.h"
61 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(menu);
64 WINE_DECLARE_DEBUG_CHANNEL(accel);
66 /* internal popup menu window messages */
68 #define MM_SETMENUHANDLE (WM_USER + 0)
69 #define MM_GETMENUHANDLE (WM_USER + 1)
71 /* Menu item structure */
72 typedef struct {
73 /* ----------- MENUITEMINFO Stuff ----------- */
74 UINT fType; /* Item type. */
75 UINT fState; /* Item state. */
76 UINT_PTR wID; /* Item id. */
77 HMENU hSubMenu; /* Pop-up menu. */
78 HBITMAP hCheckBit; /* Bitmap when checked. */
79 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
80 LPWSTR text; /* Item text. */
81 ULONG_PTR dwItemData; /* Application defined. */
82 LPWSTR dwTypeData; /* depends on fMask */
83 HBITMAP hbmpItem; /* bitmap */
84 /* ----------- Wine stuff ----------- */
85 RECT rect; /* Item area (relative to menu window) */
86 UINT xTab; /* X position of text after Tab */
87 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
88 * bitmap */
89 } MENUITEM;
91 /* Popup menu structure */
92 typedef struct {
93 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD wMagic; /* Magic number */
95 WORD Width; /* Width of the whole menu */
96 WORD Height; /* Height of the whole menu */
97 UINT nItems; /* Number of items in the menu */
98 HWND hWnd; /* Window containing the menu */
99 MENUITEM *items; /* Array of menu items */
100 UINT FocusedItem; /* Currently focused item */
101 HWND hwndOwner; /* window receiving the messages for ownerdraw */
102 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
103 BOOL bScrolling; /* Scroll arrows are active */
104 UINT nScrollPos; /* Current scroll position */
105 UINT nTotalHeight; /* Total height of menu items inside menu */
106 /* ------------ MENUINFO members ------ */
107 DWORD dwStyle; /* Extended menu style */
108 UINT cyMax; /* max height of the whole menu, 0 is screen height */
109 HBRUSH hbrBack; /* brush for menu background */
110 DWORD dwContextHelpID;
111 DWORD dwMenuData; /* application defined value */
112 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
113 SIZE maxBmpSize; /* Maximum size of the bitmap items */
114 } POPUPMENU, *LPPOPUPMENU;
116 /* internal flags for menu tracking */
118 #define TF_ENDMENU 0x0001
119 #define TF_SUSPENDPOPUP 0x0002
120 #define TF_SKIPREMOVE 0x0004
122 typedef struct
124 UINT trackFlags;
125 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
126 HMENU hTopMenu; /* initial menu */
127 HWND hOwnerWnd; /* where notifications are sent */
128 POINT pt;
129 } MTRACKER;
131 #define MENU_MAGIC 0x554d /* 'MU' */
133 #define ITEM_PREV -1
134 #define ITEM_NEXT 1
136 /* Internal MENU_TrackMenu() flags */
137 #define TPM_INTERNAL 0xF0000000
138 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
139 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
140 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
142 /* Space between 2 columns */
143 #define MENU_COL_SPACE 4
145 /* top and bottom margins for popup menus */
146 #define MENU_TOP_MARGIN 3
147 #define MENU_BOTTOM_MARGIN 2
149 /* (other menu->FocusedItem values give the position of the focused item) */
150 #define NO_SELECTED_ITEM 0xffff
152 #define MENU_ITEM_TYPE(flags) \
153 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
155 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
156 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
157 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
159 #define IS_SYSTEM_MENU(menu) \
160 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
162 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
163 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
164 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
165 MF_POPUP | MF_SYSMENU | MF_HELP)
166 #define STATE_MASK (~TYPE_MASK)
168 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
170 static SIZE menucharsize;
171 static UINT ODitemheight; /* default owner drawn item height */
173 /* Use global popup window because there's no way 2 menus can
174 * be tracked at the same time. */
175 static HWND top_popup;
177 /* Flag set by EndMenu() to force an exit from menu tracking */
178 static BOOL fEndMenu = FALSE;
180 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
182 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
184 /*********************************************************************
185 * menu class descriptor
187 const struct builtin_class_descr MENU_builtin_class =
189 POPUPMENU_CLASS_ATOMA, /* name */
190 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
191 NULL, /* procA (winproc is Unicode only) */
192 PopupMenuWndProc, /* procW */
193 sizeof(HMENU), /* extra */
194 IDC_ARROW, /* cursor */
195 (HBRUSH)(COLOR_MENU+1) /* brush */
199 /***********************************************************************
200 * debug_print_menuitem
202 * Print a menuitem in readable form.
205 #define debug_print_menuitem(pre, mp, post) \
206 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
208 #define MENUOUT(text) \
209 TRACE("%s%s", (count++ ? "," : ""), (text))
211 #define MENUFLAG(bit,text) \
212 do { \
213 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
214 } while (0)
216 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
217 const char *postfix)
219 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
220 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "HBMMENU_MBAR_CLOSE",
221 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
222 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
223 TRACE("%s ", prefix);
224 if (mp) {
225 UINT flags = mp->fType;
226 TRACE( "{ ID=0x%x", mp->wID);
227 if ( mp->hSubMenu)
228 TRACE( ", Sub=%p", mp->hSubMenu);
229 if (flags) {
230 int count = 0;
231 TRACE( ", fType=");
232 MENUFLAG( MFT_SEPARATOR, "sep");
233 MENUFLAG( MFT_OWNERDRAW, "own");
234 MENUFLAG( MFT_BITMAP, "bit");
235 MENUFLAG(MF_POPUP, "pop");
236 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
237 MENUFLAG(MFT_MENUBREAK, "brk");
238 MENUFLAG(MFT_RADIOCHECK, "radio");
239 MENUFLAG(MFT_RIGHTORDER, "rorder");
240 MENUFLAG(MF_SYSMENU, "sys");
241 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
242 if (flags)
243 TRACE( "+0x%x", flags);
245 flags = mp->fState;
246 if (flags) {
247 int count = 0;
248 TRACE( ", State=");
249 MENUFLAG(MFS_GRAYED, "grey");
250 MENUFLAG(MFS_DEFAULT, "default");
251 MENUFLAG(MFS_DISABLED, "dis");
252 MENUFLAG(MFS_CHECKED, "check");
253 MENUFLAG(MFS_HILITE, "hi");
254 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
255 MENUFLAG(MF_MOUSESELECT, "mouse");
256 if (flags)
257 TRACE( "+0x%x", flags);
259 if (mp->hCheckBit)
260 TRACE( ", Chk=%p", mp->hCheckBit);
261 if (mp->hUnCheckBit)
262 TRACE( ", Unc=%p", mp->hUnCheckBit);
263 if (mp->text)
264 TRACE( ", Text=%s", debugstr_w(mp->text));
265 if (mp->dwItemData)
266 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
267 if (mp->hbmpItem)
269 if( IS_MAGIC_BITMAP(mp->hbmpItem))
270 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
271 else
272 TRACE( ", hbitmap=%p", mp->hbmpItem);
274 TRACE( " }");
275 } else
276 TRACE( "NULL");
277 TRACE(" %s\n", postfix);
280 #undef MENUOUT
281 #undef MENUFLAG
284 /***********************************************************************
285 * MENU_GetMenu
287 * Validate the given menu handle and returns the menu structure pointer.
289 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
291 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
292 if (!menu || menu->wMagic != MENU_MAGIC)
294 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
295 menu = NULL;
297 return menu;
300 /***********************************************************************
301 * get_win_sys_menu
303 * Get the system menu of a window
305 static HMENU get_win_sys_menu( HWND hwnd )
307 HMENU ret = 0;
308 WND *win = WIN_GetPtr( hwnd );
309 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
311 ret = win->hSysMenu;
312 WIN_ReleasePtr( win );
314 return ret;
317 /***********************************************************************
318 * get_menu_font
320 static HFONT get_menu_font( BOOL bold )
322 static HFONT hMenuFont, hMenuFontBold;
324 HFONT ret = bold ? hMenuFontBold : hMenuFont;
326 if (!ret)
328 NONCLIENTMETRICSW ncm;
329 HFONT prev;
331 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
332 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
334 if (bold)
336 ncm.lfMenuFont.lfWeight += 300;
337 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
339 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
340 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
341 ret, NULL );
342 if (prev)
344 /* another thread beat us to it */
345 DeleteObject( ret );
346 ret = prev;
349 return ret;
352 /***********************************************************************
353 * get_arrow_bitmap
355 static HBITMAP get_arrow_bitmap(void)
357 static HBITMAP arrow_bitmap;
359 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
360 return arrow_bitmap;
363 /***********************************************************************
364 * get_down_arrow_bitmap
366 static HBITMAP get_down_arrow_bitmap(void)
368 static HBITMAP arrow_bitmap;
370 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
371 return arrow_bitmap;
374 /***********************************************************************
375 * get_down_arrow_inactive_bitmap
377 static HBITMAP get_down_arrow_inactive_bitmap(void)
379 static HBITMAP arrow_bitmap;
381 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
382 return arrow_bitmap;
385 /***********************************************************************
386 * get_up_arrow_bitmap
388 static HBITMAP get_up_arrow_bitmap(void)
390 static HBITMAP arrow_bitmap;
392 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
393 return arrow_bitmap;
396 /***********************************************************************
397 * get_up_arrow_inactive_bitmap
399 static HBITMAP get_up_arrow_inactive_bitmap(void)
401 static HBITMAP arrow_bitmap;
403 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
404 return arrow_bitmap;
407 /***********************************************************************
408 * MENU_CopySysPopup
410 * Return the default system menu.
412 static HMENU MENU_CopySysPopup(void)
414 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
415 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
417 if( hMenu ) {
418 POPUPMENU* menu = MENU_GetMenu(hMenu);
419 menu->wFlags |= MF_SYSMENU | MF_POPUP;
420 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
422 else
423 ERR("Unable to load default system menu\n" );
425 TRACE("returning %p.\n", hMenu );
427 return hMenu;
431 /**********************************************************************
432 * MENU_GetSysMenu
434 * Create a copy of the system menu. System menu in Windows is
435 * a special menu bar with the single entry - system menu popup.
436 * This popup is presented to the outside world as a "system menu".
437 * However, the real system menu handle is sometimes seen in the
438 * WM_MENUSELECT parameters (and Word 6 likes it this way).
440 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
442 HMENU hMenu;
444 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
445 if ((hMenu = CreateMenu()))
447 POPUPMENU *menu = MENU_GetMenu(hMenu);
448 menu->wFlags = MF_SYSMENU;
449 menu->hWnd = WIN_GetFullHandle( hWnd );
450 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
452 if (!hPopupMenu)
453 hPopupMenu = MENU_CopySysPopup();
455 if (hPopupMenu)
457 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
458 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
460 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
461 (UINT_PTR)hPopupMenu, NULL );
463 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
464 menu->items[0].fState = 0;
465 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
467 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
468 return hMenu;
470 DestroyMenu( hMenu );
472 ERR("failed to load system menu!\n");
473 return 0;
477 /***********************************************************************
478 * MENU_InitSysMenuPopup
480 * Grey the appropriate items in System menu.
482 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
484 BOOL gray;
486 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
487 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
488 gray = ((style & WS_MAXIMIZE) != 0);
489 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
490 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
491 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
492 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
493 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
494 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
495 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
496 gray = (clsStyle & CS_NOCLOSE) != 0;
498 /* The menu item must keep its state if it's disabled */
499 if(gray)
500 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
504 /******************************************************************************
506 * UINT MENU_GetStartOfNextColumn(
507 * HMENU hMenu )
509 *****************************************************************************/
511 static UINT MENU_GetStartOfNextColumn(
512 HMENU hMenu )
514 POPUPMENU *menu = MENU_GetMenu(hMenu);
515 UINT i;
517 if(!menu)
518 return NO_SELECTED_ITEM;
520 i = menu->FocusedItem + 1;
521 if( i == NO_SELECTED_ITEM )
522 return i;
524 for( ; i < menu->nItems; ++i ) {
525 if (menu->items[i].fType & MF_MENUBARBREAK)
526 return i;
529 return NO_SELECTED_ITEM;
533 /******************************************************************************
535 * UINT MENU_GetStartOfPrevColumn(
536 * HMENU hMenu )
538 *****************************************************************************/
540 static UINT MENU_GetStartOfPrevColumn(
541 HMENU hMenu )
543 POPUPMENU *menu = MENU_GetMenu(hMenu);
544 UINT i;
546 if( !menu )
547 return NO_SELECTED_ITEM;
549 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
550 return NO_SELECTED_ITEM;
552 /* Find the start of the column */
554 for(i = menu->FocusedItem; i != 0 &&
555 !(menu->items[i].fType & MF_MENUBARBREAK);
556 --i); /* empty */
558 if(i == 0)
559 return NO_SELECTED_ITEM;
561 for(--i; i != 0; --i) {
562 if (menu->items[i].fType & MF_MENUBARBREAK)
563 break;
566 TRACE("ret %d.\n", i );
568 return i;
573 /***********************************************************************
574 * MENU_FindItem
576 * Find a menu item. Return a pointer on the item, and modifies *hmenu
577 * in case the item was in a sub-menu.
579 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
581 POPUPMENU *menu;
582 MENUITEM *fallback = NULL;
583 UINT i;
585 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
586 if (wFlags & MF_BYPOSITION)
588 if (*nPos >= menu->nItems) return NULL;
589 return &menu->items[*nPos];
591 else
593 MENUITEM *item = menu->items;
594 for (i = 0; i < menu->nItems; i++, item++)
596 if (item->fType & MF_POPUP)
598 HMENU hsubmenu = item->hSubMenu;
599 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
600 if (subitem)
602 *hmenu = hsubmenu;
603 return subitem;
605 if ((UINT_PTR)item->hSubMenu == *nPos)
606 fallback = item; /* fallback to this item if nothing else found */
608 else if (item->wID == *nPos)
610 *nPos = i;
611 return item;
615 return fallback;
618 /***********************************************************************
619 * MENU_FindSubMenu
621 * Find a Sub menu. Return the position of the submenu, and modifies
622 * *hmenu in case it is found in another sub-menu.
623 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
625 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
627 POPUPMENU *menu;
628 UINT i;
629 MENUITEM *item;
630 if (((*hmenu)==(HMENU)0xffff) ||
631 (!(menu = MENU_GetMenu(*hmenu))))
632 return NO_SELECTED_ITEM;
633 item = menu->items;
634 for (i = 0; i < menu->nItems; i++, item++) {
635 if(!(item->fType & MF_POPUP)) continue;
636 if (item->hSubMenu == hSubTarget) {
637 return i;
639 else {
640 HMENU hsubmenu = item->hSubMenu;
641 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
642 if (pos != NO_SELECTED_ITEM) {
643 *hmenu = hsubmenu;
644 return pos;
648 return NO_SELECTED_ITEM;
651 /***********************************************************************
652 * MENU_FreeItemData
654 static void MENU_FreeItemData( MENUITEM* item )
656 /* delete text */
657 HeapFree( GetProcessHeap(), 0, item->text );
660 /***********************************************************************
661 * MENU_AdjustMenuItemRect
663 * Adjust menu item rectangle according to scrolling state.
665 static void
666 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
668 if (menu->bScrolling)
670 UINT arrow_bitmap_width, arrow_bitmap_height;
671 BITMAP bmp;
673 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
674 arrow_bitmap_width = bmp.bmWidth;
675 arrow_bitmap_height = bmp.bmHeight;
676 rect->top += arrow_bitmap_height - menu->nScrollPos;
677 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
682 /***********************************************************************
683 * MENU_FindItemByCoords
685 * Find the item at the specified coordinates (screen coords). Does
686 * not work for child windows and therefore should not be called for
687 * an arbitrary system menu.
689 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
690 POINT pt, UINT *pos )
692 MENUITEM *item;
693 UINT i;
694 RECT wrect;
695 RECT rect;
697 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
698 pt.x -= wrect.left;pt.y -= wrect.top;
699 item = menu->items;
700 for (i = 0; i < menu->nItems; i++, item++)
702 rect = item->rect;
703 MENU_AdjustMenuItemRect(menu, &rect);
704 if ((pt.x >= rect.left) && (pt.x < rect.right) &&
705 (pt.y >= rect.top) && (pt.y < rect.bottom))
707 if (pos) *pos = i;
708 return item;
711 return NULL;
715 /***********************************************************************
716 * MENU_FindItemByKey
718 * Find the menu item selected by a key press.
719 * Return item id, -1 if none, -2 if we should close the menu.
721 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
722 WCHAR key, BOOL forceMenuChar )
724 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
726 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
728 if (hmenu)
730 POPUPMENU *menu = MENU_GetMenu( hmenu );
731 MENUITEM *item = menu->items;
732 LRESULT menuchar;
734 if( !forceMenuChar )
736 UINT i;
738 for (i = 0; i < menu->nItems; i++, item++)
740 if( item->text)
742 WCHAR *p = item->text - 2;
745 p = strchrW (p + 2, '&');
747 while (p != NULL && p [1] == '&');
748 if (p && (toupperW(p[1]) == toupperW(key))) return i;
752 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
753 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
754 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
755 if (HIWORD(menuchar) == 1) return (UINT)(-2);
757 return (UINT)(-1);
761 /***********************************************************************
762 * MENU_GetBitmapItemSize
764 * Get the size of a bitmap item.
766 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
767 HWND hwndOwner)
769 BITMAP bm;
770 HBITMAP bmp = lpitem->hbmpItem;
772 size->cx = size->cy = 0;
774 /* check if there is a magic menu item associated with this item */
775 switch( (INT_PTR) bmp )
777 case (INT_PTR)HBMMENU_CALLBACK:
779 MEASUREITEMSTRUCT measItem;
780 measItem.CtlType = ODT_MENU;
781 measItem.CtlID = 0;
782 measItem.itemID = lpitem->wID;
783 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
784 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
785 measItem.itemData = lpitem->dwItemData;
786 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
787 size->cx = measItem.itemWidth;
788 size->cy = measItem.itemHeight;
789 return;
791 break;
792 case (INT_PTR)HBMMENU_SYSTEM:
793 if (lpitem->dwItemData)
795 bmp = (HBITMAP)lpitem->dwItemData;
796 break;
798 /* fall through */
799 case (INT_PTR)HBMMENU_MBAR_RESTORE:
800 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
801 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
802 case (INT_PTR)HBMMENU_MBAR_CLOSE:
803 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
804 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
805 size->cy = size->cx;
806 return;
807 case (INT_PTR)HBMMENU_POPUP_CLOSE:
808 case (INT_PTR)HBMMENU_POPUP_RESTORE:
809 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
810 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
811 FIXME("Magic %p not implemented\n", bmp );
812 return;
814 if (GetObjectW(bmp, sizeof(bm), &bm ))
816 size->cx = bm.bmWidth;
817 size->cy = bm.bmHeight;
821 /***********************************************************************
822 * MENU_DrawBitmapItem
824 * Draw a bitmap item.
826 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
827 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
829 BITMAP bm;
830 DWORD rop;
831 HDC hdcMem;
832 HBITMAP bmp;
833 int w = rect->right - rect->left;
834 int h = rect->bottom - rect->top;
835 int bmp_xoffset = 0;
836 int left, top;
837 HBITMAP hbmToDraw = lpitem->hbmpItem;
838 bmp = hbmToDraw;
840 /* Check if there is a magic menu item associated with this item */
841 if (IS_MAGIC_BITMAP(hbmToDraw))
843 UINT flags = 0;
844 RECT r;
846 switch((INT_PTR)hbmToDraw)
848 case (INT_PTR)HBMMENU_SYSTEM:
849 if (lpitem->dwItemData)
851 bmp = (HBITMAP)lpitem->dwItemData;
852 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
854 else
856 static HBITMAP hBmpSysMenu;
858 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
859 bmp = hBmpSysMenu;
860 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
861 /* only use right half of the bitmap */
862 bmp_xoffset = bm.bmWidth / 2;
863 bm.bmWidth -= bmp_xoffset;
865 goto got_bitmap;
866 case (INT_PTR)HBMMENU_MBAR_RESTORE:
867 flags = DFCS_CAPTIONRESTORE;
868 break;
869 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
870 flags = DFCS_CAPTIONMIN;
871 break;
872 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
873 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
874 break;
875 case (INT_PTR)HBMMENU_MBAR_CLOSE:
876 flags = DFCS_CAPTIONCLOSE;
877 break;
878 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
879 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
880 break;
881 case (INT_PTR)HBMMENU_CALLBACK:
883 DRAWITEMSTRUCT drawItem;
884 drawItem.CtlType = ODT_MENU;
885 drawItem.CtlID = 0;
886 drawItem.itemID = lpitem->wID;
887 drawItem.itemAction = odaction;
888 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
889 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
890 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
891 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
892 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
893 drawItem.hwndItem = (HWND)hmenu;
894 drawItem.hDC = hdc;
895 drawItem.itemData = lpitem->dwItemData;
896 drawItem.rcItem = *rect;
897 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
898 return;
900 break;
901 case (INT_PTR)HBMMENU_POPUP_CLOSE:
902 case (INT_PTR)HBMMENU_POPUP_RESTORE:
903 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
904 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
905 default:
906 FIXME("Magic %p not implemented\n", hbmToDraw);
907 return;
909 r = *rect;
910 InflateRect( &r, -1, -1 );
911 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
912 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
913 return;
916 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
918 got_bitmap:
919 hdcMem = CreateCompatibleDC( hdc );
920 SelectObject( hdcMem, bmp );
922 /* handle fontsize > bitmap_height */
923 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
924 left=rect->left;
925 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
926 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
927 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
928 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
929 DeleteDC( hdcMem );
933 /***********************************************************************
934 * MENU_CalcItemSize
936 * Calculate the size of the menu item and store it in lpitem->rect.
938 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
939 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
941 WCHAR *p;
942 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
943 UINT arrow_bitmap_width;
944 BITMAP bm;
945 INT itemheight;
947 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
948 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
949 (menuBar ? " (MenuBar)" : ""));
951 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
952 arrow_bitmap_width = bm.bmWidth;
954 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
955 if( !menucharsize.cx ) {
956 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
957 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
958 * but it is unlikely an application will depend on that */
959 ODitemheight = HIWORD( GetDialogBaseUnits());
962 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
964 if (lpitem->fType & MF_OWNERDRAW)
966 MEASUREITEMSTRUCT mis;
967 mis.CtlType = ODT_MENU;
968 mis.CtlID = 0;
969 mis.itemID = lpitem->wID;
970 mis.itemData = lpitem->dwItemData;
971 mis.itemHeight = ODitemheight;
972 mis.itemWidth = 0;
973 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
974 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
975 * width of a menufont character to the width of an owner-drawn menu.
977 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
978 if (menuBar) {
979 /* under at least win95 you seem to be given a standard
980 height for the menu and the height value is ignored */
981 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
982 } else
983 lpitem->rect.bottom += mis.itemHeight;
985 TRACE("id=%04x size=%ldx%ld\n",
986 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
987 lpitem->rect.bottom-lpitem->rect.top);
988 return;
991 if (lpitem->fType & MF_SEPARATOR)
993 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
994 if( !menuBar)
995 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
996 return;
999 itemheight = 0;
1000 lpitem->xTab = 0;
1002 if (!menuBar) {
1003 if (lpitem->hbmpItem) {
1004 SIZE size;
1006 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1007 /* Keep the size of the bitmap in callback mode to be able
1008 * to draw it correctly */
1009 lpitem->bmpsize = size;
1010 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1011 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1012 lpitem->rect.right += size.cx + 2;
1013 itemheight = size.cy + 2;
1015 if( !(lppop->dwStyle & MNS_NOCHECK))
1016 lpitem->rect.right += check_bitmap_width;
1017 lpitem->rect.right += 4 + menucharsize.cx;
1018 lpitem->xTab = lpitem->rect.right;
1019 lpitem->rect.right += arrow_bitmap_width;
1020 } else if (lpitem->hbmpItem) { /* menuBar */
1021 SIZE size;
1023 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1024 lpitem->bmpsize = size;
1025 lpitem->rect.right += size.cx;
1026 if( lpitem->text) lpitem->rect.right += 2;
1027 itemheight = size.cy;
1030 /* it must be a text item - unless it's the system menu */
1031 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1032 HFONT hfontOld = NULL;
1033 RECT rc = lpitem->rect;
1034 LONG txtheight, txtwidth;
1036 if ( lpitem->fState & MFS_DEFAULT ) {
1037 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1039 if (menuBar) {
1040 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1041 DT_SINGLELINE|DT_CALCRECT);
1042 lpitem->rect.right += rc.right - rc.left;
1043 itemheight = max( max( itemheight, txtheight),
1044 GetSystemMetrics( SM_CYMENU) - 1);
1045 lpitem->rect.right += 2 * menucharsize.cx;
1046 } else {
1047 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1048 RECT tmprc = rc;
1049 LONG tmpheight;
1050 int n = (int)( p - lpitem->text);
1051 /* Item contains a tab (only meaningful in popup menus) */
1052 /* get text size before the tab */
1053 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1054 DT_SINGLELINE|DT_CALCRECT);
1055 txtwidth = rc.right - rc.left;
1056 p += 1; /* advance past the Tab */
1057 /* get text size after the tab */
1058 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1059 DT_SINGLELINE|DT_CALCRECT);
1060 lpitem->xTab += txtwidth;
1061 txtheight = max( txtheight, tmpheight);
1062 txtwidth += menucharsize.cx + /* space for the tab */
1063 tmprc.right - tmprc.left; /* space for the short cut */
1064 } else {
1065 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1066 DT_SINGLELINE|DT_CALCRECT);
1067 txtwidth = rc.right - rc.left;
1068 lpitem->xTab += txtwidth;
1070 lpitem->rect.right += 2 + txtwidth;
1071 itemheight = max( itemheight,
1072 max( txtheight + 2, menucharsize.cy + 4));
1074 if (hfontOld) SelectObject (hdc, hfontOld);
1075 } else if( menuBar) {
1076 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1078 lpitem->rect.bottom += itemheight;
1079 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1083 /***********************************************************************
1084 * MENU_GetMaxPopupHeight
1086 static UINT
1087 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1089 if (lppop->cyMax)
1090 return lppop->cyMax;
1091 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1095 /***********************************************************************
1096 * MENU_PopupMenuCalcSize
1098 * Calculate the size of a popup menu.
1100 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1102 MENUITEM *lpitem;
1103 HDC hdc;
1104 int start, i;
1105 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1107 lppop->Width = lppop->Height = 0;
1108 if (lppop->nItems == 0) return;
1109 hdc = GetDC( 0 );
1111 SelectObject( hdc, get_menu_font(FALSE));
1113 start = 0;
1114 maxX = 2 + 1;
1116 lppop->maxBmpSize.cx = 0;
1117 lppop->maxBmpSize.cy = 0;
1119 while (start < lppop->nItems)
1121 lpitem = &lppop->items[start];
1122 orgX = maxX;
1123 if( lpitem->fType & MF_MENUBREAK)
1124 orgX += MENU_COL_SPACE;
1125 orgY = MENU_TOP_MARGIN;
1127 maxTab = maxTabWidth = 0;
1128 /* Parse items until column break or end of menu */
1129 for (i = start; i < lppop->nItems; i++, lpitem++)
1131 if ((i != start) &&
1132 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1134 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1136 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1137 maxX = max( maxX, lpitem->rect.right );
1138 orgY = lpitem->rect.bottom;
1139 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1141 maxTab = max( maxTab, lpitem->xTab );
1142 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1146 /* Finish the column (set all items to the largest width found) */
1147 maxX = max( maxX, maxTab + maxTabWidth );
1148 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1150 lpitem->rect.right = maxX;
1151 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1152 lpitem->xTab = maxTab;
1155 lppop->Height = max( lppop->Height, orgY );
1158 lppop->Width = maxX;
1160 /* space for 3d border */
1161 lppop->Height += MENU_BOTTOM_MARGIN;
1162 lppop->Width += 2;
1164 /* Adjust popup height if it exceeds maximum */
1165 maxHeight = MENU_GetMaxPopupHeight(lppop);
1166 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1167 if (lppop->Height >= maxHeight)
1169 lppop->Height = maxHeight;
1170 lppop->bScrolling = TRUE;
1172 else
1174 lppop->bScrolling = FALSE;
1177 ReleaseDC( 0, hdc );
1181 /***********************************************************************
1182 * MENU_MenuBarCalcSize
1184 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1185 * height is off by 1 pixel which causes lengthy window relocations when
1186 * active document window is maximized/restored.
1188 * Calculate the size of the menu bar.
1190 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1191 LPPOPUPMENU lppop, HWND hwndOwner )
1193 MENUITEM *lpitem;
1194 int start, i, orgX, orgY, maxY, helpPos;
1196 if ((lprect == NULL) || (lppop == NULL)) return;
1197 if (lppop->nItems == 0) return;
1198 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1199 lppop->Width = lprect->right - lprect->left;
1200 lppop->Height = 0;
1201 maxY = lprect->top+1;
1202 start = 0;
1203 helpPos = -1;
1204 lppop->maxBmpSize.cx = 0;
1205 lppop->maxBmpSize.cy = 0;
1206 while (start < lppop->nItems)
1208 lpitem = &lppop->items[start];
1209 orgX = lprect->left;
1210 orgY = maxY;
1212 /* Parse items until line break or end of menu */
1213 for (i = start; i < lppop->nItems; i++, lpitem++)
1215 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1216 if ((i != start) &&
1217 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1219 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1220 debug_print_menuitem (" item: ", lpitem, "");
1221 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1223 if (lpitem->rect.right > lprect->right)
1225 if (i != start) break;
1226 else lpitem->rect.right = lprect->right;
1228 maxY = max( maxY, lpitem->rect.bottom );
1229 orgX = lpitem->rect.right;
1232 /* Finish the line (set all items to the largest height found) */
1233 while (start < i) lppop->items[start++].rect.bottom = maxY;
1236 lprect->bottom = maxY;
1237 lppop->Height = lprect->bottom - lprect->top;
1239 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1240 /* the last item (if several lines, only move the last line) */
1241 lpitem = &lppop->items[lppop->nItems-1];
1242 orgY = lpitem->rect.top;
1243 orgX = lprect->right;
1244 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1245 if ( (helpPos==-1) || (helpPos>i) )
1246 break; /* done */
1247 if (lpitem->rect.top != orgY) break; /* Other line */
1248 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1249 lpitem->rect.left += orgX - lpitem->rect.right;
1250 lpitem->rect.right = orgX;
1251 orgX = lpitem->rect.left;
1256 /***********************************************************************
1257 * MENU_DrawScrollArrows
1259 * Draw scroll arrows.
1261 static void
1262 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1264 HDC hdcMem = CreateCompatibleDC(hdc);
1265 HBITMAP hOrigBitmap;
1266 UINT arrow_bitmap_width, arrow_bitmap_height;
1267 BITMAP bmp;
1268 RECT rect;
1270 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1271 arrow_bitmap_width = bmp.bmWidth;
1272 arrow_bitmap_height = bmp.bmHeight;
1275 if (lppop->nScrollPos)
1276 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1277 else
1278 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1279 rect.left = 0;
1280 rect.top = 0;
1281 rect.right = lppop->Width;
1282 rect.bottom = arrow_bitmap_height;
1283 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1284 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1285 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1286 rect.top = lppop->Height - arrow_bitmap_height;
1287 rect.bottom = lppop->Height;
1288 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1289 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1290 SelectObject(hdcMem, get_down_arrow_bitmap());
1291 else
1292 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1293 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1294 lppop->Height - arrow_bitmap_height,
1295 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1296 SelectObject(hdcMem, hOrigBitmap);
1297 DeleteDC(hdcMem);
1301 /***********************************************************************
1302 * draw_popup_arrow
1304 * Draws the popup-menu arrow.
1306 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1307 UINT arrow_bitmap_height)
1309 HDC hdcMem = CreateCompatibleDC( hdc );
1310 HBITMAP hOrigBitmap;
1312 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1313 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1314 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1315 arrow_bitmap_width, arrow_bitmap_height,
1316 hdcMem, 0, 0, SRCCOPY );
1317 SelectObject( hdcMem, hOrigBitmap );
1318 DeleteDC( hdcMem );
1320 /***********************************************************************
1321 * MENU_DrawMenuItem
1323 * Draw a single menu item.
1325 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1326 UINT height, BOOL menuBar, UINT odaction )
1328 RECT rect;
1329 BOOL flat_menu = FALSE;
1330 int bkgnd;
1331 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1332 POPUPMENU *menu = MENU_GetMenu(hmenu);
1333 RECT bmprc;
1335 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1337 if (!menuBar) {
1338 BITMAP bmp;
1339 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1340 arrow_bitmap_width = bmp.bmWidth;
1341 arrow_bitmap_height = bmp.bmHeight;
1344 if (lpitem->fType & MF_SYSMENU)
1346 if( !IsIconic(hwnd) )
1347 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1348 return;
1351 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1352 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1354 /* Setup colors */
1356 if (lpitem->fState & MF_HILITE)
1358 if(menuBar && !flat_menu) {
1359 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1360 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1361 } else {
1362 if(lpitem->fState & MF_GRAYED)
1363 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1364 else
1365 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1366 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1369 else
1371 if (lpitem->fState & MF_GRAYED)
1372 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1373 else
1374 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1375 SetBkColor( hdc, GetSysColor( bkgnd ) );
1378 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1379 rect = lpitem->rect;
1380 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1382 if (lpitem->fType & MF_OWNERDRAW)
1385 ** Experimentation under Windows reveals that an owner-drawn
1386 ** menu is given the rectangle which includes the space it requested
1387 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1388 ** and a popup-menu arrow. This is the value of lpitem->rect.
1389 ** Windows will leave all drawing to the application except for
1390 ** the popup-menu arrow. Windows always draws that itself, after
1391 ** the menu owner has finished drawing.
1393 DRAWITEMSTRUCT dis;
1395 dis.CtlType = ODT_MENU;
1396 dis.CtlID = 0;
1397 dis.itemID = lpitem->wID;
1398 dis.itemData = lpitem->dwItemData;
1399 dis.itemState = 0;
1400 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1401 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1402 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1403 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1404 dis.hwndItem = (HWND)hmenu;
1405 dis.hDC = hdc;
1406 dis.rcItem = rect;
1407 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1408 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1409 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1410 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1411 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1412 /* Draw the popup-menu arrow */
1413 if (lpitem->fType & MF_POPUP)
1414 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1415 arrow_bitmap_height);
1416 return;
1419 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1421 if (lpitem->fState & MF_HILITE)
1423 if (flat_menu)
1425 InflateRect (&rect, -1, -1);
1426 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1427 InflateRect (&rect, 1, 1);
1428 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1430 else
1432 if(menuBar)
1433 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1434 else
1435 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1438 else
1439 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1441 SetBkMode( hdc, TRANSPARENT );
1443 /* vertical separator */
1444 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1446 HPEN oldPen;
1447 RECT rc = rect;
1449 rc.top = 3;
1450 rc.bottom = height - 3;
1451 if (flat_menu)
1453 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1454 MoveToEx( hdc, rc.left, rc.top, NULL );
1455 LineTo( hdc, rc.left, rc.bottom );
1456 SelectObject( hdc, oldPen );
1458 else
1459 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1462 /* horizontal separator */
1463 if (lpitem->fType & MF_SEPARATOR)
1465 HPEN oldPen;
1466 RECT rc = rect;
1468 rc.left++;
1469 rc.right--;
1470 rc.top = ( rc.top + rc.bottom) / 2;
1471 if (flat_menu)
1473 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1474 MoveToEx( hdc, rc.left, rc.top, NULL );
1475 LineTo( hdc, rc.right, rc.top );
1476 SelectObject( hdc, oldPen );
1478 else
1479 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1480 return;
1483 /* helper lines for debugging */
1484 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1485 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1486 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1487 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1490 if (lpitem->hbmpItem) {
1491 /* calculate the bitmap rectangle in coordinates relative
1492 * to the item rectangle */
1493 if( menuBar) {
1494 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1495 bmprc.left = 3;
1496 else
1497 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1498 } else {
1499 bmprc.left = 4;
1500 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1501 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1503 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1504 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1505 bmprc.top = 0;
1506 else
1507 bmprc.top = (lpitem->rect.bottom - lpitem->rect.top -
1508 lpitem->bmpsize.cy) / 2;
1509 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1512 if (!menuBar)
1514 HBITMAP bm;
1515 INT y = rect.top + rect.bottom;
1516 RECT rc = rect;
1517 int checked = FALSE;
1518 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1519 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1520 /* Draw the check mark
1522 * FIXME:
1523 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1525 if( !(menu->dwStyle & MNS_NOCHECK)) {
1526 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1527 lpitem->hUnCheckBit;
1528 if (bm) /* we have a custom bitmap */
1530 HDC hdcMem = CreateCompatibleDC( hdc );
1532 SelectObject( hdcMem, bm );
1533 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1534 check_bitmap_width, check_bitmap_height,
1535 hdcMem, 0, 0, SRCCOPY );
1536 DeleteDC( hdcMem );
1537 checked = TRUE;
1539 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1541 RECT r;
1542 HBITMAP bm = CreateBitmap( check_bitmap_width,
1543 check_bitmap_height, 1, 1, NULL );
1544 HDC hdcMem = CreateCompatibleDC( hdc );
1546 SelectObject( hdcMem, bm );
1547 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1548 DrawFrameControl( hdcMem, &r, DFC_MENU,
1549 (lpitem->fType & MFT_RADIOCHECK) ?
1550 DFCS_MENUBULLET : DFCS_MENUCHECK );
1551 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1552 hdcMem, 0, 0, SRCCOPY );
1553 DeleteDC( hdcMem );
1554 DeleteObject( bm );
1555 checked = TRUE;
1558 if( lpitem->hbmpItem &&
1559 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1560 POINT origorg;
1561 /* some applications make this assumption on the DC's origin */
1562 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1563 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1564 odaction, FALSE);
1565 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1567 /* Draw the popup-menu arrow */
1568 if (lpitem->fType & MF_POPUP)
1569 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1570 arrow_bitmap_height);
1571 rect.left += 4;
1572 if( !(menu->dwStyle & MNS_NOCHECK))
1573 rect.left += check_bitmap_width;
1574 rect.right -= arrow_bitmap_width;
1576 else if( lpitem->hbmpItem)
1577 { /* Draw the bitmap */
1578 POINT origorg;
1580 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1581 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1582 odaction, menuBar);
1583 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1585 /* process text if present */
1586 if (lpitem->text)
1588 register int i;
1589 HFONT hfontOld = 0;
1591 UINT uFormat = (menuBar) ?
1592 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1593 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1595 if( !(menu->dwStyle & MNS_CHECKORBMP))
1596 rect.left += menu->maxBmpSize.cx;
1598 if ( lpitem->fState & MFS_DEFAULT )
1600 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1603 if (menuBar) {
1604 if( lpitem->hbmpItem)
1605 rect.left += lpitem->bmpsize.cx;
1606 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1607 rect.left += menucharsize.cx;
1608 rect.right -= menucharsize.cx;
1611 for (i = 0; lpitem->text[i]; i++)
1612 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1613 break;
1615 if(lpitem->fState & MF_GRAYED)
1617 if (!(lpitem->fState & MF_HILITE) )
1619 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1620 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1621 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1622 --rect.left; --rect.top; --rect.right; --rect.bottom;
1624 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1627 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1629 /* paint the shortcut text */
1630 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1632 if (lpitem->text[i] == '\t')
1634 rect.left = lpitem->xTab;
1635 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1637 else
1639 rect.right = lpitem->xTab;
1640 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1643 if(lpitem->fState & MF_GRAYED)
1645 if (!(lpitem->fState & MF_HILITE) )
1647 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1648 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1649 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1650 --rect.left; --rect.top; --rect.right; --rect.bottom;
1652 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1654 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1657 if (hfontOld)
1658 SelectObject (hdc, hfontOld);
1663 /***********************************************************************
1664 * MENU_DrawPopupMenu
1666 * Paint a popup menu.
1668 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1670 HBRUSH hPrevBrush = 0;
1671 RECT rect;
1673 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1675 GetClientRect( hwnd, &rect );
1677 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1678 && (SelectObject( hdc, get_menu_font(FALSE))))
1680 HPEN hPrevPen;
1682 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1684 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1685 if( hPrevPen )
1687 POPUPMENU *menu;
1688 BOOL flat_menu = FALSE;
1690 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1691 if (flat_menu)
1692 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1693 else
1694 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1696 menu = MENU_GetMenu( hmenu );
1698 /* draw menu items */
1699 if (menu && menu->nItems)
1701 MENUITEM *item;
1702 UINT u;
1704 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1705 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1706 menu->Height, FALSE, ODA_DRAWENTIRE );
1710 /* draw scroll arrows */
1711 if (menu->bScrolling)
1712 MENU_DrawScrollArrows(menu, hdc);
1713 } else
1715 SelectObject( hdc, hPrevBrush );
1720 /***********************************************************************
1721 * MENU_DrawMenuBar
1723 * Paint a menu bar. Returns the height of the menu bar.
1724 * called from [windows/nonclient.c]
1726 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1727 BOOL suppress_draw)
1729 LPPOPUPMENU lppop;
1730 HFONT hfontOld = 0;
1731 HMENU hMenu = GetMenu(hwnd);
1733 lppop = MENU_GetMenu( hMenu );
1734 if (lppop == NULL || lprect == NULL)
1736 return GetSystemMetrics(SM_CYMENU);
1739 if (suppress_draw)
1741 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1743 if (lppop->Height == 0)
1744 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1746 lprect->bottom = lprect->top + lppop->Height;
1748 if (hfontOld) SelectObject( hDC, hfontOld);
1749 return lppop->Height;
1751 else
1752 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1756 /***********************************************************************
1757 * MENU_ShowPopup
1759 * Display a popup menu.
1761 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1762 INT x, INT y, INT xanchor, INT yanchor )
1764 POPUPMENU *menu;
1765 UINT width, height;
1767 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1768 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1770 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1771 if (menu->FocusedItem != NO_SELECTED_ITEM)
1773 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1774 menu->FocusedItem = NO_SELECTED_ITEM;
1777 /* store the owner for DrawItem */
1778 menu->hwndOwner = hwndOwner;
1780 menu->nScrollPos = 0;
1781 MENU_PopupMenuCalcSize( menu, hwndOwner );
1783 /* adjust popup menu pos so that it fits within the desktop */
1785 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1786 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1788 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1790 if( xanchor )
1791 x -= width - xanchor;
1792 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1793 x = GetSystemMetrics(SM_CXSCREEN) - width;
1795 if( x < 0 ) x = 0;
1797 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1799 if( yanchor )
1800 y -= height + yanchor;
1801 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1802 y = GetSystemMetrics(SM_CYSCREEN) - height;
1804 if( y < 0 ) y = 0;
1806 /* NOTE: In Windows, top menu popup is not owned. */
1807 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1808 WS_POPUP, x, y, width, height,
1809 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1810 (LPVOID)hmenu );
1811 if( !menu->hWnd ) return FALSE;
1812 if (!top_popup) top_popup = menu->hWnd;
1814 /* Display the window */
1816 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1817 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1818 UpdateWindow( menu->hWnd );
1819 return TRUE;
1823 /***********************************************************************
1824 * MENU_EnsureMenuItemVisible
1826 static void
1827 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1829 if (lppop->bScrolling)
1831 MENUITEM *item = &lppop->items[wIndex];
1832 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1833 UINT nOldPos = lppop->nScrollPos;
1834 RECT rc;
1835 UINT arrow_bitmap_height;
1836 BITMAP bmp;
1838 GetClientRect(lppop->hWnd, &rc);
1840 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1841 arrow_bitmap_height = bmp.bmHeight;
1843 rc.top += arrow_bitmap_height;
1844 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1846 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1847 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1850 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1851 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1852 MENU_DrawScrollArrows(lppop, hdc);
1854 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1856 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1857 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1858 MENU_DrawScrollArrows(lppop, hdc);
1864 /***********************************************************************
1865 * MENU_SelectItem
1867 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1868 BOOL sendMenuSelect, HMENU topmenu )
1870 LPPOPUPMENU lppop;
1871 HDC hdc;
1873 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1875 lppop = MENU_GetMenu( hmenu );
1876 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1878 if (lppop->FocusedItem == wIndex) return;
1879 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1880 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1881 if (!top_popup) top_popup = lppop->hWnd;
1883 SelectObject( hdc, get_menu_font(FALSE));
1885 /* Clear previous highlighted item */
1886 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1888 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1889 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1890 lppop->Height, !(lppop->wFlags & MF_POPUP),
1891 ODA_SELECT );
1894 /* Highlight new item (if any) */
1895 lppop->FocusedItem = wIndex;
1896 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1898 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1899 lppop->items[wIndex].fState |= MF_HILITE;
1900 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1901 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1902 &lppop->items[wIndex], lppop->Height,
1903 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1905 if (sendMenuSelect)
1907 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1908 SendMessageW( hwndOwner, WM_MENUSELECT,
1909 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1910 ip->fType | ip->fState |
1911 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1914 else if (sendMenuSelect) {
1915 if(topmenu){
1916 int pos;
1917 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1918 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1919 MENUITEM *ip = &ptm->items[pos];
1920 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1921 ip->fType | ip->fState |
1922 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1926 ReleaseDC( lppop->hWnd, hdc );
1930 /***********************************************************************
1931 * MENU_MoveSelection
1933 * Moves currently selected item according to the offset parameter.
1934 * If there is no selection then it should select the last item if
1935 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1937 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1939 INT i;
1940 POPUPMENU *menu;
1942 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1944 menu = MENU_GetMenu( hmenu );
1945 if ((!menu) || (!menu->items)) return;
1947 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1949 if( menu->nItems == 1 ) return; else
1950 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1951 ; i += offset)
1952 if (!(menu->items[i].fType & MF_SEPARATOR))
1954 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1955 return;
1959 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1960 i >= 0 && i < menu->nItems ; i += offset)
1961 if (!(menu->items[i].fType & MF_SEPARATOR))
1963 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1964 return;
1969 /**********************************************************************
1970 * MENU_SetItemData
1972 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1973 * ModifyMenu().
1975 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1976 LPCWSTR str )
1978 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1979 TRACE("flags=%x str=%p\n", flags, str);
1981 if (IS_STRING_ITEM(flags))
1983 LPWSTR prevText = item->text;
1984 if (!str)
1986 flags |= MF_SEPARATOR;
1987 item->text = NULL;
1989 else
1991 LPWSTR text;
1992 /* Item beginning with a backspace is a help item */
1993 if (*str == '\b')
1995 flags |= MF_HELP;
1996 str++;
1998 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1999 return FALSE;
2000 strcpyW( text, str );
2001 item->text = text;
2003 item->hbmpItem = NULL;
2004 HeapFree( GetProcessHeap(), 0, prevText );
2006 else if(( flags & MFT_BITMAP)) {
2007 item->hbmpItem = HBITMAP_32(LOWORD(str));
2008 /* setting bitmap clears text */
2009 HeapFree( GetProcessHeap(), 0, item->text );
2010 item->text = NULL;
2013 if (flags & MF_OWNERDRAW)
2014 item->dwItemData = (DWORD_PTR)str;
2015 else
2016 item->dwItemData = 0;
2018 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2019 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2021 if (flags & MF_POPUP)
2023 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2024 if (menu) menu->wFlags |= MF_POPUP;
2025 else
2027 item->wID = 0;
2028 item->hSubMenu = 0;
2029 item->fType = 0;
2030 item->fState = 0;
2031 return FALSE;
2035 item->wID = id;
2036 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2038 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2039 flags |= MF_POPUP; /* keep popup */
2041 item->fType = flags & TYPE_MASK;
2042 item->fState = (flags & STATE_MASK) &
2043 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
2045 /* Don't call SetRectEmpty here! */
2047 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2048 return TRUE;
2052 /**********************************************************************
2053 * MENU_InsertItem
2055 * Insert (allocate) a new item into a menu.
2057 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2059 MENUITEM *newItems;
2060 POPUPMENU *menu;
2062 if (!(menu = MENU_GetMenu(hMenu)))
2063 return NULL;
2065 /* Find where to insert new item */
2067 if (flags & MF_BYPOSITION) {
2068 if (pos > menu->nItems)
2069 pos = menu->nItems;
2070 } else {
2071 if (!MENU_FindItem( &hMenu, &pos, flags ))
2072 pos = menu->nItems;
2073 else {
2074 if (!(menu = MENU_GetMenu( hMenu )))
2075 return NULL;
2079 /* Create new items array */
2081 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2082 if (!newItems)
2084 WARN("allocation failed\n" );
2085 return NULL;
2087 if (menu->nItems > 0)
2089 /* Copy the old array into the new one */
2090 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2091 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2092 (menu->nItems-pos)*sizeof(MENUITEM) );
2093 HeapFree( GetProcessHeap(), 0, menu->items );
2095 menu->items = newItems;
2096 menu->nItems++;
2097 memset( &newItems[pos], 0, sizeof(*newItems) );
2098 menu->Height = 0; /* force size recalculate */
2099 return &newItems[pos];
2103 /**********************************************************************
2104 * MENU_ParseResource
2106 * Parse a standard menu resource and add items to the menu.
2107 * Return a pointer to the end of the resource.
2109 * NOTE: flags is equivalent to the mtOption field
2111 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2113 WORD flags, id = 0;
2114 LPCSTR str;
2118 flags = GET_WORD(res);
2119 res += sizeof(WORD);
2120 if (!(flags & MF_POPUP))
2122 id = GET_WORD(res);
2123 res += sizeof(WORD);
2125 str = res;
2126 if (!unicode) res += strlen(str) + 1;
2127 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2128 if (flags & MF_POPUP)
2130 HMENU hSubMenu = CreatePopupMenu();
2131 if (!hSubMenu) return NULL;
2132 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2133 return NULL;
2134 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2135 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2137 else /* Not a popup */
2139 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2140 else AppendMenuW( hMenu, flags, id,
2141 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2143 } while (!(flags & MF_END));
2144 return res;
2148 /**********************************************************************
2149 * MENUEX_ParseResource
2151 * Parse an extended menu resource and add items to the menu.
2152 * Return a pointer to the end of the resource.
2154 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2156 WORD resinfo;
2157 do {
2158 MENUITEMINFOW mii;
2160 mii.cbSize = sizeof(mii);
2161 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2162 mii.fType = GET_DWORD(res);
2163 res += sizeof(DWORD);
2164 mii.fState = GET_DWORD(res);
2165 res += sizeof(DWORD);
2166 mii.wID = GET_DWORD(res);
2167 res += sizeof(DWORD);
2168 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2169 res += sizeof(WORD);
2170 /* Align the text on a word boundary. */
2171 res += (~((UINT_PTR)res - 1)) & 1;
2172 mii.dwTypeData = (LPWSTR) res;
2173 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2174 /* Align the following fields on a dword boundary. */
2175 res += (~((UINT_PTR)res - 1)) & 3;
2177 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2178 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2180 if (resinfo & 1) { /* Pop-up? */
2181 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2182 res += sizeof(DWORD);
2183 mii.hSubMenu = CreatePopupMenu();
2184 if (!mii.hSubMenu)
2185 return NULL;
2186 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2187 DestroyMenu(mii.hSubMenu);
2188 return NULL;
2190 mii.fMask |= MIIM_SUBMENU;
2191 mii.fType |= MF_POPUP;
2193 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2195 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2196 mii.wID, mii.fType);
2197 mii.fType |= MF_SEPARATOR;
2199 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2200 } while (!(resinfo & MF_END));
2201 return res;
2205 /***********************************************************************
2206 * MENU_GetSubPopup
2208 * Return the handle of the selected sub-popup menu (if any).
2210 static HMENU MENU_GetSubPopup( HMENU hmenu )
2212 POPUPMENU *menu;
2213 MENUITEM *item;
2215 menu = MENU_GetMenu( hmenu );
2217 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2219 item = &menu->items[menu->FocusedItem];
2220 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2221 return item->hSubMenu;
2222 return 0;
2226 /***********************************************************************
2227 * MENU_HideSubPopups
2229 * Hide the sub-popup menus of this menu.
2231 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2232 BOOL sendMenuSelect )
2234 POPUPMENU *menu = MENU_GetMenu( hmenu );
2236 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2238 if (menu && top_popup)
2240 HMENU hsubmenu;
2241 POPUPMENU *submenu;
2242 MENUITEM *item;
2244 if (menu->FocusedItem != NO_SELECTED_ITEM)
2246 item = &menu->items[menu->FocusedItem];
2247 if (!(item->fType & MF_POPUP) ||
2248 !(item->fState & MF_MOUSESELECT)) return;
2249 item->fState &= ~MF_MOUSESELECT;
2250 hsubmenu = item->hSubMenu;
2251 } else return;
2253 submenu = MENU_GetMenu( hsubmenu );
2254 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2255 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2256 DestroyWindow( submenu->hWnd );
2257 submenu->hWnd = 0;
2262 /***********************************************************************
2263 * MENU_ShowSubPopup
2265 * Display the sub-menu of the selected item of this menu.
2266 * Return the handle of the submenu, or hmenu if no submenu to display.
2268 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2269 BOOL selectFirst, UINT wFlags )
2271 RECT rect;
2272 POPUPMENU *menu;
2273 MENUITEM *item;
2274 HDC hdc;
2276 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2278 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2280 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2282 item = &menu->items[menu->FocusedItem];
2283 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2284 return hmenu;
2286 /* message must be sent before using item,
2287 because nearly everything may be changed by the application ! */
2289 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2290 if (!(wFlags & TPM_NONOTIFY))
2291 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2292 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2294 item = &menu->items[menu->FocusedItem];
2295 rect = item->rect;
2297 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2298 if (!(item->fState & MF_HILITE))
2300 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2301 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2303 SelectObject( hdc, get_menu_font(FALSE));
2305 item->fState |= MF_HILITE;
2306 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2307 ReleaseDC( menu->hWnd, hdc );
2309 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2310 item->rect = rect;
2312 item->fState |= MF_MOUSESELECT;
2314 if (IS_SYSTEM_MENU(menu))
2316 MENU_InitSysMenuPopup(item->hSubMenu,
2317 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2318 GetClassLongW( menu->hWnd, GCL_STYLE));
2320 NC_GetSysPopupPos( menu->hWnd, &rect );
2321 rect.top = rect.bottom;
2322 rect.right = GetSystemMetrics(SM_CXSIZE);
2323 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2325 else
2327 GetWindowRect( menu->hWnd, &rect );
2328 if (menu->wFlags & MF_POPUP)
2330 RECT rc = item->rect;
2332 MENU_AdjustMenuItemRect(menu, &rc);
2333 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2334 rect.top += rc.top;
2335 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2336 rect.bottom = rc.top - rc.bottom;
2338 else
2340 rect.left += item->rect.left;
2341 rect.top += item->rect.bottom;
2342 rect.right = item->rect.right - item->rect.left;
2343 rect.bottom = item->rect.bottom - item->rect.top;
2347 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2348 rect.left, rect.top, rect.right, rect.bottom );
2349 if (selectFirst)
2350 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2351 return item->hSubMenu;
2356 /**********************************************************************
2357 * MENU_IsMenuActive
2359 HWND MENU_IsMenuActive(void)
2361 return top_popup;
2364 /***********************************************************************
2365 * MENU_PtMenu
2367 * Walks menu chain trying to find a menu pt maps to.
2369 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2371 POPUPMENU *menu = MENU_GetMenu( hMenu );
2372 UINT item = menu->FocusedItem;
2373 HMENU ret;
2375 /* try subpopup first (if any) */
2376 ret = (item != NO_SELECTED_ITEM &&
2377 (menu->items[item].fType & MF_POPUP) &&
2378 (menu->items[item].fState & MF_MOUSESELECT))
2379 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2381 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2383 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2384 if( menu->wFlags & MF_POPUP )
2386 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2388 else if (ht == HTSYSMENU)
2389 ret = get_win_sys_menu( menu->hWnd );
2390 else if (ht == HTMENU)
2391 ret = GetMenu( menu->hWnd );
2393 return ret;
2396 /***********************************************************************
2397 * MENU_ExecFocusedItem
2399 * Execute a menu item (for instance when user pressed Enter).
2400 * Return the wID of the executed item. Otherwise, -1 indicating
2401 * that no menu item was executed;
2402 * Have to receive the flags for the TrackPopupMenu options to avoid
2403 * sending unwanted message.
2406 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2408 MENUITEM *item;
2409 POPUPMENU *menu = MENU_GetMenu( hMenu );
2411 TRACE("%p hmenu=%p\n", pmt, hMenu);
2413 if (!menu || !menu->nItems ||
2414 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2416 item = &menu->items[menu->FocusedItem];
2418 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2420 if (!(item->fType & MF_POPUP))
2422 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2424 /* If TPM_RETURNCMD is set you return the id, but
2425 do not send a message to the owner */
2426 if(!(wFlags & TPM_RETURNCMD))
2428 if( menu->wFlags & MF_SYSMENU )
2429 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2430 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2431 else
2432 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2434 return item->wID;
2437 else
2438 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2440 return -1;
2443 /***********************************************************************
2444 * MENU_SwitchTracking
2446 * Helper function for menu navigation routines.
2448 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2450 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2451 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2453 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2455 if( pmt->hTopMenu != hPtMenu &&
2456 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2458 /* both are top level menus (system and menu-bar) */
2459 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2460 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2461 pmt->hTopMenu = hPtMenu;
2463 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2464 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2468 /***********************************************************************
2469 * MENU_ButtonDown
2471 * Return TRUE if we can go on with menu tracking.
2473 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2475 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2477 if (hPtMenu)
2479 UINT id = 0;
2480 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2481 MENUITEM *item;
2483 if( IS_SYSTEM_MENU(ptmenu) )
2484 item = ptmenu->items;
2485 else
2486 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2488 if( item )
2490 if( ptmenu->FocusedItem != id )
2491 MENU_SwitchTracking( pmt, hPtMenu, id );
2493 /* If the popup menu is not already "popped" */
2494 if(!(item->fState & MF_MOUSESELECT ))
2496 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2499 return TRUE;
2501 /* Else the click was on the menu bar, finish the tracking */
2503 return FALSE;
2506 /***********************************************************************
2507 * MENU_ButtonUp
2509 * Return the value of MENU_ExecFocusedItem if
2510 * the selected item was not a popup. Else open the popup.
2511 * A -1 return value indicates that we go on with menu tracking.
2514 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2516 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2518 if (hPtMenu)
2520 UINT id = 0;
2521 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2522 MENUITEM *item;
2524 if( IS_SYSTEM_MENU(ptmenu) )
2525 item = ptmenu->items;
2526 else
2527 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2529 if( item && (ptmenu->FocusedItem == id ))
2531 if( !(item->fType & MF_POPUP) )
2532 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2534 /* If we are dealing with the top-level menu */
2535 /* and this is a click on an already "popped" item: */
2536 /* Stop the menu tracking and close the opened submenus */
2537 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2538 return 0;
2540 ptmenu->bTimeToHide = TRUE;
2542 return -1;
2546 /***********************************************************************
2547 * MENU_MouseMove
2549 * Return TRUE if we can go on with menu tracking.
2551 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2553 UINT id = NO_SELECTED_ITEM;
2554 POPUPMENU *ptmenu = NULL;
2556 if( hPtMenu )
2558 ptmenu = MENU_GetMenu( hPtMenu );
2559 if( IS_SYSTEM_MENU(ptmenu) )
2560 id = 0;
2561 else
2562 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2565 if( id == NO_SELECTED_ITEM )
2567 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2568 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2571 else if( ptmenu->FocusedItem != id )
2573 MENU_SwitchTracking( pmt, hPtMenu, id );
2574 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2576 return TRUE;
2580 /***********************************************************************
2581 * MENU_SetCapture
2583 static void MENU_SetCapture( HWND hwnd )
2585 HWND previous = 0;
2587 SERVER_START_REQ( set_capture_window )
2589 req->handle = hwnd;
2590 req->flags = CAPTURE_MENU;
2591 if (!wine_server_call_err( req ))
2593 previous = reply->previous;
2594 hwnd = reply->full_handle;
2597 SERVER_END_REQ;
2599 if (previous && previous != hwnd)
2600 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2604 /***********************************************************************
2605 * MENU_DoNextMenu
2607 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2609 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2611 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2613 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2614 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2616 MDINEXTMENU next_menu;
2617 HMENU hNewMenu;
2618 HWND hNewWnd;
2619 UINT id = 0;
2621 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2622 next_menu.hmenuNext = 0;
2623 next_menu.hwndNext = 0;
2624 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2626 TRACE("%p [%p] -> %p [%p]\n",
2627 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2629 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2631 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2632 hNewWnd = pmt->hOwnerWnd;
2633 if( IS_SYSTEM_MENU(menu) )
2635 /* switch to the menu bar */
2637 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2639 if( vk == VK_LEFT )
2641 menu = MENU_GetMenu( hNewMenu );
2642 id = menu->nItems - 1;
2645 else if (style & WS_SYSMENU )
2647 /* switch to the system menu */
2648 hNewMenu = get_win_sys_menu( hNewWnd );
2650 else return FALSE;
2652 else /* application returned a new menu to switch to */
2654 hNewMenu = next_menu.hmenuNext;
2655 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2657 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2659 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2661 if (style & WS_SYSMENU &&
2662 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2664 /* get the real system menu */
2665 hNewMenu = get_win_sys_menu(hNewWnd);
2667 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2669 /* FIXME: Not sure what to do here;
2670 * perhaps try to track hNewMenu as a popup? */
2672 TRACE(" -- got confused.\n");
2673 return FALSE;
2676 else return FALSE;
2679 if( hNewMenu != pmt->hTopMenu )
2681 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2682 FALSE, 0 );
2683 if( pmt->hCurrentMenu != pmt->hTopMenu )
2684 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2687 if( hNewWnd != pmt->hOwnerWnd )
2689 pmt->hOwnerWnd = hNewWnd;
2690 MENU_SetCapture( pmt->hOwnerWnd );
2693 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2694 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2696 return TRUE;
2698 return FALSE;
2701 /***********************************************************************
2702 * MENU_SuspendPopup
2704 * The idea is not to show the popup if the next input message is
2705 * going to hide it anyway.
2707 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2709 MSG msg;
2711 msg.hwnd = pmt->hOwnerWnd;
2713 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2714 pmt->trackFlags |= TF_SKIPREMOVE;
2716 switch( uMsg )
2718 case WM_KEYDOWN:
2719 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2720 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2722 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2723 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2724 if( msg.message == WM_KEYDOWN &&
2725 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2727 pmt->trackFlags |= TF_SUSPENDPOPUP;
2728 return TRUE;
2731 break;
2734 /* failures go through this */
2735 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2736 return FALSE;
2739 /***********************************************************************
2740 * MENU_KeyEscape
2742 * Handle a VK_ESCAPE key event in a menu.
2744 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2746 BOOL bEndMenu = TRUE;
2748 if (pmt->hCurrentMenu != pmt->hTopMenu)
2750 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2752 if (menu->wFlags & MF_POPUP)
2754 HMENU hmenutmp, hmenuprev;
2756 hmenuprev = hmenutmp = pmt->hTopMenu;
2758 /* close topmost popup */
2759 while (hmenutmp != pmt->hCurrentMenu)
2761 hmenuprev = hmenutmp;
2762 hmenutmp = MENU_GetSubPopup( hmenuprev );
2765 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2766 pmt->hCurrentMenu = hmenuprev;
2767 bEndMenu = FALSE;
2771 return bEndMenu;
2774 /***********************************************************************
2775 * MENU_KeyLeft
2777 * Handle a VK_LEFT key event in a menu.
2779 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2781 POPUPMENU *menu;
2782 HMENU hmenutmp, hmenuprev;
2783 UINT prevcol;
2785 hmenuprev = hmenutmp = pmt->hTopMenu;
2786 menu = MENU_GetMenu( hmenutmp );
2788 /* Try to move 1 column left (if possible) */
2789 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2790 NO_SELECTED_ITEM ) {
2792 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2793 prevcol, TRUE, 0 );
2794 return;
2797 /* close topmost popup */
2798 while (hmenutmp != pmt->hCurrentMenu)
2800 hmenuprev = hmenutmp;
2801 hmenutmp = MENU_GetSubPopup( hmenuprev );
2804 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2805 pmt->hCurrentMenu = hmenuprev;
2807 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2809 /* move menu bar selection if no more popups are left */
2811 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2812 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2814 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2816 /* A sublevel menu was displayed - display the next one
2817 * unless there is another displacement coming up */
2819 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2820 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2821 pmt->hTopMenu, TRUE, wFlags);
2827 /***********************************************************************
2828 * MENU_KeyRight
2830 * Handle a VK_RIGHT key event in a menu.
2832 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2834 HMENU hmenutmp;
2835 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2836 UINT nextcol;
2838 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2839 pmt->hCurrentMenu,
2840 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2841 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2843 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2845 /* If already displaying a popup, try to display sub-popup */
2847 hmenutmp = pmt->hCurrentMenu;
2848 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2850 /* if subpopup was displayed then we are done */
2851 if (hmenutmp != pmt->hCurrentMenu) return;
2854 /* Check to see if there's another column */
2855 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2856 NO_SELECTED_ITEM ) {
2857 TRACE("Going to %d.\n", nextcol );
2858 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2859 nextcol, TRUE, 0 );
2860 return;
2863 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2865 if( pmt->hCurrentMenu != pmt->hTopMenu )
2867 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2868 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2869 } else hmenutmp = 0;
2871 /* try to move to the next item */
2872 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2873 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2875 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2876 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2877 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2878 pmt->hTopMenu, TRUE, wFlags);
2882 /***********************************************************************
2883 * MENU_TrackMenu
2885 * Menu tracking code.
2887 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2888 HWND hwnd, const RECT *lprect )
2890 MSG msg;
2891 POPUPMENU *menu;
2892 BOOL fRemove;
2893 INT executedMenuId = -1;
2894 MTRACKER mt;
2895 BOOL enterIdleSent = FALSE;
2897 mt.trackFlags = 0;
2898 mt.hCurrentMenu = hmenu;
2899 mt.hTopMenu = hmenu;
2900 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2901 mt.pt.x = x;
2902 mt.pt.y = y;
2904 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2905 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2907 fEndMenu = FALSE;
2908 if (!(menu = MENU_GetMenu( hmenu )))
2910 WARN("Invalid menu handle %p\n", hmenu);
2911 SetLastError(ERROR_INVALID_MENU_HANDLE);
2912 return FALSE;
2915 if (wFlags & TPM_BUTTONDOWN)
2917 /* Get the result in order to start the tracking or not */
2918 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2919 fEndMenu = !fRemove;
2922 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2924 MENU_SetCapture( mt.hOwnerWnd );
2926 while (!fEndMenu)
2928 menu = MENU_GetMenu( mt.hCurrentMenu );
2929 if (!menu) /* sometimes happens if I do a window manager close */
2930 break;
2932 /* we have to keep the message in the queue until it's
2933 * clear that menu loop is not over yet. */
2935 for (;;)
2937 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2939 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2940 /* remove the message from the queue */
2941 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2943 else
2945 if (!enterIdleSent)
2947 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2948 enterIdleSent = TRUE;
2949 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2951 WaitMessage();
2955 /* check if EndMenu() tried to cancel us, by posting this message */
2956 if(msg.message == WM_CANCELMODE)
2958 /* we are now out of the loop */
2959 fEndMenu = TRUE;
2961 /* remove the message from the queue */
2962 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2964 /* break out of internal loop, ala ESCAPE */
2965 break;
2968 TranslateMessage( &msg );
2969 mt.pt = msg.pt;
2971 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2972 enterIdleSent=FALSE;
2974 fRemove = FALSE;
2975 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2978 * Use the mouse coordinates in lParam instead of those in the MSG
2979 * struct to properly handle synthetic messages. They are already
2980 * in screen coordinates.
2982 mt.pt.x = (short)LOWORD(msg.lParam);
2983 mt.pt.y = (short)HIWORD(msg.lParam);
2985 /* Find a menu for this mouse event */
2986 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2988 switch(msg.message)
2990 /* no WM_NC... messages in captured state */
2992 case WM_RBUTTONDBLCLK:
2993 case WM_RBUTTONDOWN:
2994 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2995 /* fall through */
2996 case WM_LBUTTONDBLCLK:
2997 case WM_LBUTTONDOWN:
2998 /* If the message belongs to the menu, removes it from the queue */
2999 /* Else, end menu tracking */
3000 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3001 fEndMenu = !fRemove;
3002 break;
3004 case WM_RBUTTONUP:
3005 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3006 /* fall through */
3007 case WM_LBUTTONUP:
3008 /* Check if a menu was selected by the mouse */
3009 if (hmenu)
3011 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3013 /* End the loop if executedMenuId is an item ID */
3014 /* or if the job was done (executedMenuId = 0). */
3015 fEndMenu = fRemove = (executedMenuId != -1);
3017 /* No menu was selected by the mouse */
3018 /* if the function was called by TrackPopupMenu, continue
3019 with the menu tracking. If not, stop it */
3020 else
3021 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3023 break;
3025 case WM_MOUSEMOVE:
3026 /* the selected menu item must be changed every time */
3027 /* the mouse moves. */
3029 if (hmenu)
3030 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3032 } /* switch(msg.message) - mouse */
3034 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3036 fRemove = TRUE; /* Keyboard messages are always removed */
3037 switch(msg.message)
3039 case WM_KEYDOWN:
3040 case WM_SYSKEYDOWN:
3041 switch(msg.wParam)
3043 case VK_MENU:
3044 fEndMenu = TRUE;
3045 break;
3047 case VK_HOME:
3048 case VK_END:
3049 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3050 NO_SELECTED_ITEM, FALSE, 0 );
3051 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3052 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3053 break;
3055 case VK_UP:
3056 case VK_DOWN: /* If on menu bar, pull-down the menu */
3058 menu = MENU_GetMenu( mt.hCurrentMenu );
3059 if (!(menu->wFlags & MF_POPUP))
3060 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3061 else /* otherwise try to move selection */
3062 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3063 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3064 break;
3066 case VK_LEFT:
3067 MENU_KeyLeft( &mt, wFlags );
3068 break;
3070 case VK_RIGHT:
3071 MENU_KeyRight( &mt, wFlags );
3072 break;
3074 case VK_ESCAPE:
3075 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3076 break;
3078 case VK_F1:
3080 HELPINFO hi;
3081 hi.cbSize = sizeof(HELPINFO);
3082 hi.iContextType = HELPINFO_MENUITEM;
3083 if (menu->FocusedItem == NO_SELECTED_ITEM)
3084 hi.iCtrlId = 0;
3085 else
3086 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3087 hi.hItemHandle = hmenu;
3088 hi.dwContextId = menu->dwContextHelpID;
3089 hi.MousePos = msg.pt;
3090 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3091 break;
3094 default:
3095 break;
3097 break; /* WM_KEYDOWN */
3099 case WM_CHAR:
3100 case WM_SYSCHAR:
3102 UINT pos;
3104 if (msg.wParam == '\r' || msg.wParam == ' ')
3106 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3107 fEndMenu = (executedMenuId != -1);
3109 break;
3112 /* Hack to avoid control chars. */
3113 /* We will find a better way real soon... */
3114 if (msg.wParam < 32) break;
3116 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3117 LOWORD(msg.wParam), FALSE );
3118 if (pos == (UINT)-2) fEndMenu = TRUE;
3119 else if (pos == (UINT)-1) MessageBeep(0);
3120 else
3122 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3123 TRUE, 0 );
3124 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3125 fEndMenu = (executedMenuId != -1);
3128 break;
3129 } /* switch(msg.message) - kbd */
3131 else
3133 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3134 DispatchMessageW( &msg );
3135 continue;
3138 if (!fEndMenu) fRemove = TRUE;
3140 /* finally remove message from the queue */
3142 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3143 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3144 else mt.trackFlags &= ~TF_SKIPREMOVE;
3147 MENU_SetCapture(0); /* release the capture */
3149 /* If dropdown is still painted and the close box is clicked on
3150 then the menu will be destroyed as part of the DispatchMessage above.
3151 This will then invalidate the menu handle in mt.hTopMenu. We should
3152 check for this first. */
3153 if( IsMenu( mt.hTopMenu ) )
3155 menu = MENU_GetMenu( mt.hTopMenu );
3157 if( IsWindow( mt.hOwnerWnd ) )
3159 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3161 if (menu && (menu->wFlags & MF_POPUP))
3163 DestroyWindow( menu->hWnd );
3164 menu->hWnd = 0;
3166 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3167 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3170 /* Reset the variable for hiding menu */
3171 if( menu ) menu->bTimeToHide = FALSE;
3174 /* The return value is only used by TrackPopupMenu */
3175 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3176 if (executedMenuId == -1) executedMenuId = 0;
3177 return executedMenuId;
3180 /***********************************************************************
3181 * MENU_InitTracking
3183 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3185 POPUPMENU *menu;
3187 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3189 HideCaret(0);
3191 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3192 if (!(wFlags & TPM_NONOTIFY))
3193 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3195 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3197 if (!(wFlags & TPM_NONOTIFY))
3199 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3200 /* If an app changed/recreated menu bar entries in WM_INITMENU
3201 * menu sizes will be recalculated once the menu created/shown.
3205 /* This makes the menus of applications built with Delphi work.
3206 * It also enables menus to be displayed in more than one window,
3207 * but there are some bugs left that need to be fixed in this case.
3209 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3211 return TRUE;
3213 /***********************************************************************
3214 * MENU_ExitTracking
3216 static BOOL MENU_ExitTracking(HWND hWnd)
3218 TRACE("hwnd=%p\n", hWnd);
3220 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3221 ShowCaret(0);
3222 top_popup = 0;
3223 return TRUE;
3226 /***********************************************************************
3227 * MENU_TrackMouseMenuBar
3229 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3231 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3233 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3234 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3236 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3238 if (IsMenu(hMenu))
3240 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3241 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3242 MENU_ExitTracking(hWnd);
3247 /***********************************************************************
3248 * MENU_TrackKbdMenuBar
3250 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3252 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3254 UINT uItem = NO_SELECTED_ITEM;
3255 HMENU hTrackMenu;
3256 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3258 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3260 /* find window that has a menu */
3262 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3263 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3265 /* check if we have to track a system menu */
3267 hTrackMenu = GetMenu( hwnd );
3268 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3270 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3271 hTrackMenu = get_win_sys_menu( hwnd );
3272 uItem = 0;
3273 wParam |= HTSYSMENU; /* prevent item lookup */
3276 if (!IsMenu( hTrackMenu )) return;
3278 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3280 if( wChar && wChar != ' ' )
3282 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3283 if ( uItem >= (UINT)(-2) )
3285 if( uItem == (UINT)(-1) ) MessageBeep(0);
3286 /* schedule end of menu tracking */
3287 wFlags |= TF_ENDMENU;
3288 goto track_menu;
3292 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3294 if (wParam & HTSYSMENU)
3296 /* prevent sysmenu activation for managed windows on Alt down/up */
3297 if (GetPropA( hwnd, "__wine_x11_managed" ))
3298 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3300 else
3302 if( uItem == NO_SELECTED_ITEM )
3303 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3304 else
3305 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3308 track_menu:
3309 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3310 MENU_ExitTracking( hwnd );
3314 /**********************************************************************
3315 * TrackPopupMenu (USER32.@)
3317 * Like the win32 API, the function return the command ID only if the
3318 * flag TPM_RETURNCMD is on.
3321 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3322 INT nReserved, HWND hWnd, const RECT *lpRect )
3324 BOOL ret = FALSE;
3326 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3328 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3329 if (!(wFlags & TPM_NONOTIFY))
3330 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3332 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3333 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3334 MENU_ExitTracking(hWnd);
3336 return ret;
3339 /**********************************************************************
3340 * TrackPopupMenuEx (USER32.@)
3342 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3343 HWND hWnd, LPTPMPARAMS lpTpm )
3345 FIXME("not fully implemented\n" );
3346 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3347 lpTpm ? &lpTpm->rcExclude : NULL );
3350 /***********************************************************************
3351 * PopupMenuWndProc
3353 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3355 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3357 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3359 switch(message)
3361 case WM_CREATE:
3363 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3364 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3365 return 0;
3368 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3369 return MA_NOACTIVATE;
3371 case WM_PAINT:
3373 PAINTSTRUCT ps;
3374 BeginPaint( hwnd, &ps );
3375 MENU_DrawPopupMenu( hwnd, ps.hdc,
3376 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3377 EndPaint( hwnd, &ps );
3378 return 0;
3380 case WM_ERASEBKGND:
3381 return 1;
3383 case WM_DESTROY:
3384 /* zero out global pointer in case resident popup window was destroyed. */
3385 if (hwnd == top_popup) top_popup = 0;
3386 break;
3388 case WM_SHOWWINDOW:
3390 if( wParam )
3392 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3394 else
3395 SetWindowLongPtrW( hwnd, 0, 0 );
3396 break;
3398 case MM_SETMENUHANDLE:
3399 SetWindowLongPtrW( hwnd, 0, wParam );
3400 break;
3402 case MM_GETMENUHANDLE:
3403 return GetWindowLongPtrW( hwnd, 0 );
3405 default:
3406 return DefWindowProcW( hwnd, message, wParam, lParam );
3408 return 0;
3412 /***********************************************************************
3413 * MENU_GetMenuBarHeight
3415 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3417 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3418 INT orgX, INT orgY )
3420 HDC hdc;
3421 RECT rectBar;
3422 LPPOPUPMENU lppop;
3424 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3426 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3428 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3429 SelectObject( hdc, get_menu_font(FALSE));
3430 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3431 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3432 ReleaseDC( hwnd, hdc );
3433 return lppop->Height;
3437 /*******************************************************************
3438 * ChangeMenuA (USER32.@)
3440 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3441 UINT id, UINT flags )
3443 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3444 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3445 id, data );
3446 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3447 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3448 id, data );
3449 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3450 flags & MF_BYPOSITION ? pos : id,
3451 flags & ~MF_REMOVE );
3452 /* Default: MF_INSERT */
3453 return InsertMenuA( hMenu, pos, flags, id, data );
3457 /*******************************************************************
3458 * ChangeMenuW (USER32.@)
3460 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3461 UINT id, UINT flags )
3463 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3464 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3465 id, data );
3466 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3467 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3468 id, data );
3469 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3470 flags & MF_BYPOSITION ? pos : id,
3471 flags & ~MF_REMOVE );
3472 /* Default: MF_INSERT */
3473 return InsertMenuW( hMenu, pos, flags, id, data );
3477 /*******************************************************************
3478 * CheckMenuItem (USER32.@)
3480 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3482 MENUITEM *item;
3483 DWORD ret;
3485 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3486 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3487 ret = item->fState & MF_CHECKED;
3488 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3489 else item->fState &= ~MF_CHECKED;
3490 return ret;
3494 /**********************************************************************
3495 * EnableMenuItem (USER32.@)
3497 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3499 UINT oldflags;
3500 MENUITEM *item;
3501 POPUPMENU *menu;
3503 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3505 /* Get the Popupmenu to access the owner menu */
3506 if (!(menu = MENU_GetMenu(hMenu)))
3507 return (UINT)-1;
3509 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3510 return (UINT)-1;
3512 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3513 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3515 /* If the close item in the system menu change update the close button */
3516 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3518 if (menu->hSysMenuOwner != 0)
3520 RECT rc;
3521 POPUPMENU* parentMenu;
3523 /* Get the parent menu to access*/
3524 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3525 return (UINT)-1;
3527 /* Refresh the frame to reflect the change */
3528 GetWindowRect(parentMenu->hWnd, &rc);
3529 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3530 rc.bottom = 0;
3531 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3535 return oldflags;
3539 /*******************************************************************
3540 * GetMenuStringA (USER32.@)
3542 INT WINAPI GetMenuStringA(
3543 HMENU hMenu, /* [in] menuhandle */
3544 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3545 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3546 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3547 UINT wFlags /* [in] MF_ flags */
3549 MENUITEM *item;
3551 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3552 if (str && nMaxSiz) str[0] = '\0';
3553 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3554 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3555 return 0;
3557 if (!item->text) return 0;
3558 if (!str || !nMaxSiz) return strlenW(item->text);
3559 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3560 str[nMaxSiz-1] = 0;
3561 TRACE("returning '%s'\n", str );
3562 return strlen(str);
3566 /*******************************************************************
3567 * GetMenuStringW (USER32.@)
3569 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3570 LPWSTR str, INT nMaxSiz, UINT wFlags )
3572 MENUITEM *item;
3574 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3575 if (str && nMaxSiz) str[0] = '\0';
3576 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3577 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3578 return 0;
3580 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3581 if( !(item->text)) {
3582 str[0] = 0;
3583 return 0;
3585 lstrcpynW( str, item->text, nMaxSiz );
3586 return strlenW(str);
3590 /**********************************************************************
3591 * HiliteMenuItem (USER32.@)
3593 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3594 UINT wHilite )
3596 LPPOPUPMENU menu;
3597 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3598 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3599 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3600 if (menu->FocusedItem == wItemID) return TRUE;
3601 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3602 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3603 return TRUE;
3607 /**********************************************************************
3608 * GetMenuState (USER32.@)
3610 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3612 MENUITEM *item;
3613 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3614 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3615 debug_print_menuitem (" item: ", item, "");
3616 if (item->fType & MF_POPUP)
3618 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3619 if (!menu) return -1;
3620 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3622 else
3624 /* We used to (from way back then) mask the result to 0xff. */
3625 /* I don't know why and it seems wrong as the documented */
3626 /* return flag MF_SEPARATOR is outside that mask. */
3627 return (item->fType | item->fState);
3632 /**********************************************************************
3633 * GetMenuItemCount (USER32.@)
3635 INT WINAPI GetMenuItemCount( HMENU hMenu )
3637 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3638 if (!menu) return -1;
3639 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3640 return menu->nItems;
3644 /**********************************************************************
3645 * GetMenuItemID (USER32.@)
3647 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3649 MENUITEM * lpmi;
3651 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3652 if (lpmi->fType & MF_POPUP) return -1;
3653 return lpmi->wID;
3658 /*******************************************************************
3659 * InsertMenuW (USER32.@)
3661 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3662 UINT_PTR id, LPCWSTR str )
3664 MENUITEM *item;
3666 if (IS_STRING_ITEM(flags) && str)
3667 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3668 hMenu, pos, flags, id, debugstr_w(str) );
3669 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3670 hMenu, pos, flags, id, str );
3672 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3674 if (!(MENU_SetItemData( item, flags, id, str )))
3676 RemoveMenu( hMenu, pos, flags );
3677 return FALSE;
3680 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3681 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3683 item->hCheckBit = item->hUnCheckBit = 0;
3684 return TRUE;
3688 /*******************************************************************
3689 * InsertMenuA (USER32.@)
3691 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3692 UINT_PTR id, LPCSTR str )
3694 BOOL ret = FALSE;
3696 if (IS_STRING_ITEM(flags) && str)
3698 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3699 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3700 if (newstr)
3702 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3703 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3704 HeapFree( GetProcessHeap(), 0, newstr );
3706 return ret;
3708 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3712 /*******************************************************************
3713 * AppendMenuA (USER32.@)
3715 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3716 UINT_PTR id, LPCSTR data )
3718 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3722 /*******************************************************************
3723 * AppendMenuW (USER32.@)
3725 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3726 UINT_PTR id, LPCWSTR data )
3728 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3732 /**********************************************************************
3733 * RemoveMenu (USER32.@)
3735 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3737 LPPOPUPMENU menu;
3738 MENUITEM *item;
3740 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3741 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3742 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3744 /* Remove item */
3746 MENU_FreeItemData( item );
3748 if (--menu->nItems == 0)
3750 HeapFree( GetProcessHeap(), 0, menu->items );
3751 menu->items = NULL;
3753 else
3755 while(nPos < menu->nItems)
3757 *item = *(item+1);
3758 item++;
3759 nPos++;
3761 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3762 menu->nItems * sizeof(MENUITEM) );
3764 return TRUE;
3768 /**********************************************************************
3769 * DeleteMenu (USER32.@)
3771 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3773 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3774 if (!item) return FALSE;
3775 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3776 /* nPos is now the position of the item */
3777 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3778 return TRUE;
3782 /*******************************************************************
3783 * ModifyMenuW (USER32.@)
3785 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3786 UINT_PTR id, LPCWSTR str )
3788 MENUITEM *item;
3790 if (IS_STRING_ITEM(flags))
3791 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3792 else
3793 TRACE("%p %d %04x %04x %p\n", hMenu, pos, flags, id, str );
3795 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3796 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3797 return MENU_SetItemData( item, flags, id, str );
3801 /*******************************************************************
3802 * ModifyMenuA (USER32.@)
3804 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3805 UINT_PTR id, LPCSTR str )
3807 BOOL ret = FALSE;
3809 if (IS_STRING_ITEM(flags) && str)
3811 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3812 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3813 if (newstr)
3815 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3816 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3817 HeapFree( GetProcessHeap(), 0, newstr );
3819 return ret;
3821 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3825 /**********************************************************************
3826 * CreatePopupMenu (USER32.@)
3828 HMENU WINAPI CreatePopupMenu(void)
3830 HMENU hmenu;
3831 POPUPMENU *menu;
3833 if (!(hmenu = CreateMenu())) return 0;
3834 menu = MENU_GetMenu( hmenu );
3835 menu->wFlags |= MF_POPUP;
3836 menu->bTimeToHide = FALSE;
3837 return hmenu;
3841 /**********************************************************************
3842 * GetMenuCheckMarkDimensions (USER.417)
3843 * GetMenuCheckMarkDimensions (USER32.@)
3845 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3847 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3851 /**********************************************************************
3852 * SetMenuItemBitmaps (USER32.@)
3854 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3855 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3857 MENUITEM *item;
3858 TRACE("(%p, %04x, %04x, %p, %p)\n",
3859 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3860 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3862 if (!hNewCheck && !hNewUnCheck)
3864 item->fState &= ~MF_USECHECKBITMAPS;
3866 else /* Install new bitmaps */
3868 item->hCheckBit = hNewCheck;
3869 item->hUnCheckBit = hNewUnCheck;
3870 item->fState |= MF_USECHECKBITMAPS;
3872 return TRUE;
3876 /**********************************************************************
3877 * CreateMenu (USER32.@)
3879 HMENU WINAPI CreateMenu(void)
3881 HMENU hMenu;
3882 LPPOPUPMENU menu;
3883 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3884 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3886 ZeroMemory(menu, sizeof(POPUPMENU));
3887 menu->wMagic = MENU_MAGIC;
3888 menu->FocusedItem = NO_SELECTED_ITEM;
3889 menu->bTimeToHide = FALSE;
3891 TRACE("return %p\n", hMenu );
3893 return hMenu;
3897 /**********************************************************************
3898 * DestroyMenu (USER32.@)
3900 BOOL WINAPI DestroyMenu( HMENU hMenu )
3902 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3904 TRACE("(%p)\n", hMenu);
3907 if (!lppop) return FALSE;
3909 lppop->wMagic = 0; /* Mark it as destroyed */
3911 /* DestroyMenu should not destroy system menu popup owner */
3912 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3914 DestroyWindow( lppop->hWnd );
3915 lppop->hWnd = 0;
3918 if (lppop->items) /* recursively destroy submenus */
3920 int i;
3921 MENUITEM *item = lppop->items;
3922 for (i = lppop->nItems; i > 0; i--, item++)
3924 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3925 MENU_FreeItemData( item );
3927 HeapFree( GetProcessHeap(), 0, lppop->items );
3929 USER_HEAP_FREE( hMenu );
3930 return TRUE;
3934 /**********************************************************************
3935 * GetSystemMenu (USER32.@)
3937 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3939 WND *wndPtr = WIN_GetPtr( hWnd );
3940 HMENU retvalue = 0;
3942 if (wndPtr == WND_DESKTOP) return 0;
3943 if (wndPtr == WND_OTHER_PROCESS)
3945 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3947 else if (wndPtr)
3949 if (wndPtr->hSysMenu && bRevert)
3951 DestroyMenu(wndPtr->hSysMenu);
3952 wndPtr->hSysMenu = 0;
3955 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3956 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
3958 if( wndPtr->hSysMenu )
3960 POPUPMENU *menu;
3961 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3963 /* Store the dummy sysmenu handle to facilitate the refresh */
3964 /* of the close button if the SC_CLOSE item change */
3965 menu = MENU_GetMenu(retvalue);
3966 if ( menu )
3967 menu->hSysMenuOwner = wndPtr->hSysMenu;
3969 WIN_ReleasePtr( wndPtr );
3971 return bRevert ? 0 : retvalue;
3975 /*******************************************************************
3976 * SetSystemMenu (USER32.@)
3978 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3980 WND *wndPtr = WIN_GetPtr( hwnd );
3982 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3984 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3985 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3986 WIN_ReleasePtr( wndPtr );
3987 return TRUE;
3989 return FALSE;
3993 /**********************************************************************
3994 * GetMenu (USER32.@)
3996 HMENU WINAPI GetMenu( HWND hWnd )
3998 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3999 TRACE("for %p returning %p\n", hWnd, retvalue);
4000 return retvalue;
4003 /**********************************************************************
4004 * GetMenuBarInfo (USER32.@)
4006 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4008 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
4009 return FALSE;
4012 /**********************************************************************
4013 * MENU_SetMenu
4015 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4016 * SetWindowPos call that would result if SetMenu were called directly.
4018 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4020 TRACE("(%p, %p);\n", hWnd, hMenu);
4022 if (hMenu && !IsMenu(hMenu))
4024 WARN("hMenu %p is not a menu handle\n", hMenu);
4025 SetLastError(ERROR_INVALID_MENU_HANDLE);
4026 return FALSE;
4028 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4029 return FALSE;
4031 hWnd = WIN_GetFullHandle( hWnd );
4032 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4034 if (hMenu != 0)
4036 LPPOPUPMENU lpmenu;
4038 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4040 lpmenu->hWnd = hWnd;
4041 lpmenu->Height = 0; /* Make sure we recalculate the size */
4043 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4044 return TRUE;
4048 /**********************************************************************
4049 * SetMenu (USER32.@)
4051 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4053 if(!MENU_SetMenu(hWnd, hMenu))
4054 return FALSE;
4056 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4057 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4058 return TRUE;
4062 /**********************************************************************
4063 * GetSubMenu (USER32.@)
4065 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4067 MENUITEM * lpmi;
4069 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4070 if (!(lpmi->fType & MF_POPUP)) return 0;
4071 return lpmi->hSubMenu;
4075 /**********************************************************************
4076 * DrawMenuBar (USER32.@)
4078 BOOL WINAPI DrawMenuBar( HWND hWnd )
4080 LPPOPUPMENU lppop;
4081 HMENU hMenu = GetMenu(hWnd);
4083 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4084 return FALSE;
4085 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4087 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4088 lppop->hwndOwner = hWnd;
4089 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4090 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4091 return TRUE;
4094 /***********************************************************************
4095 * DrawMenuBarTemp (USER32.@)
4097 * UNDOCUMENTED !!
4099 * called by W98SE desk.cpl Control Panel Applet
4101 * Not 100% sure about the param names, but close.
4103 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4105 LPPOPUPMENU lppop;
4106 UINT i,retvalue;
4107 HFONT hfontOld = 0;
4108 BOOL flat_menu = FALSE;
4110 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4112 if (!hMenu)
4113 hMenu = GetMenu(hwnd);
4115 if (!hFont)
4116 hFont = get_menu_font(FALSE);
4118 lppop = MENU_GetMenu( hMenu );
4119 if (lppop == NULL || lprect == NULL)
4121 retvalue = GetSystemMetrics(SM_CYMENU);
4122 goto END;
4125 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4127 hfontOld = SelectObject( hDC, hFont);
4129 if (lppop->Height == 0)
4130 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4132 lprect->bottom = lprect->top + lppop->Height;
4134 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4136 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4137 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4138 LineTo( hDC, lprect->right, lprect->bottom );
4140 if (lppop->nItems == 0)
4142 retvalue = GetSystemMetrics(SM_CYMENU);
4143 goto END;
4146 for (i = 0; i < lppop->nItems; i++)
4148 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4149 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4151 retvalue = lppop->Height;
4153 END:
4154 if (hfontOld) SelectObject (hDC, hfontOld);
4155 return retvalue;
4158 /***********************************************************************
4159 * EndMenu (USER.187)
4160 * EndMenu (USER32.@)
4162 void WINAPI EndMenu(void)
4164 /* if we are in the menu code, and it is active */
4165 if (!fEndMenu && top_popup)
4167 /* terminate the menu handling code */
4168 fEndMenu = TRUE;
4170 /* needs to be posted to wakeup the internal menu handler */
4171 /* which will now terminate the menu, in the event that */
4172 /* the main window was minimized, or lost focus, so we */
4173 /* don't end up with an orphaned menu */
4174 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4179 /***********************************************************************
4180 * LookupMenuHandle (USER.217)
4182 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4184 HMENU hmenu32 = HMENU_32(hmenu);
4185 UINT id32 = id;
4186 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4187 else return HMENU_16(hmenu32);
4191 /**********************************************************************
4192 * LoadMenu (USER.150)
4194 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4196 HRSRC16 hRsrc;
4197 HGLOBAL16 handle;
4198 HMENU16 hMenu;
4200 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4201 if (!name) return 0;
4203 instance = GetExePtr( instance );
4204 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4205 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4206 hMenu = LoadMenuIndirect16(LockResource16(handle));
4207 FreeResource16( handle );
4208 return hMenu;
4212 /*****************************************************************
4213 * LoadMenuA (USER32.@)
4215 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4217 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4218 if (!hrsrc) return 0;
4219 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4223 /*****************************************************************
4224 * LoadMenuW (USER32.@)
4226 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4228 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4229 if (!hrsrc) return 0;
4230 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4234 /**********************************************************************
4235 * LoadMenuIndirect (USER.220)
4237 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4239 HMENU hMenu;
4240 WORD version, offset;
4241 LPCSTR p = (LPCSTR)template;
4243 TRACE("(%p)\n", template );
4244 version = GET_WORD(p);
4245 p += sizeof(WORD);
4246 if (version)
4248 WARN("version must be 0 for Win16\n" );
4249 return 0;
4251 offset = GET_WORD(p);
4252 p += sizeof(WORD) + offset;
4253 if (!(hMenu = CreateMenu())) return 0;
4254 if (!MENU_ParseResource( p, hMenu, FALSE ))
4256 DestroyMenu( hMenu );
4257 return 0;
4259 return HMENU_16(hMenu);
4263 /**********************************************************************
4264 * LoadMenuIndirectW (USER32.@)
4266 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4268 HMENU hMenu;
4269 WORD version, offset;
4270 LPCSTR p = (LPCSTR)template;
4272 version = GET_WORD(p);
4273 p += sizeof(WORD);
4274 TRACE("%p, ver %d\n", template, version );
4275 switch (version)
4277 case 0: /* standard format is version of 0 */
4278 offset = GET_WORD(p);
4279 p += sizeof(WORD) + offset;
4280 if (!(hMenu = CreateMenu())) return 0;
4281 if (!MENU_ParseResource( p, hMenu, TRUE ))
4283 DestroyMenu( hMenu );
4284 return 0;
4286 return hMenu;
4287 case 1: /* extended format is version of 1 */
4288 offset = GET_WORD(p);
4289 p += sizeof(WORD) + offset;
4290 if (!(hMenu = CreateMenu())) return 0;
4291 if (!MENUEX_ParseResource( p, hMenu))
4293 DestroyMenu( hMenu );
4294 return 0;
4296 return hMenu;
4297 default:
4298 ERR("version %d not supported.\n", version);
4299 return 0;
4304 /**********************************************************************
4305 * LoadMenuIndirectA (USER32.@)
4307 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4309 return LoadMenuIndirectW( template );
4313 /**********************************************************************
4314 * IsMenu (USER32.@)
4316 BOOL WINAPI IsMenu(HMENU hmenu)
4318 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4319 return menu != NULL;
4322 /**********************************************************************
4323 * GetMenuItemInfo_common
4326 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4327 LPMENUITEMINFOW lpmii, BOOL unicode)
4329 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4331 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4333 if (!menu)
4334 return FALSE;
4336 if( lpmii->fMask & MIIM_TYPE) {
4337 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4338 WARN("invalid combination of fMask bits used\n");
4339 /* this does not happen on Win9x/ME */
4340 SetLastError( ERROR_INVALID_PARAMETER);
4341 return FALSE;
4343 lpmii->fType = menu->fType & ~MF_POPUP;
4344 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4345 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4346 if( lpmii->fType & MFT_BITMAP) {
4347 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4348 lpmii->cch = 0;
4349 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4350 /* this does not happen on Win9x/ME */
4351 lpmii->dwTypeData = 0;
4352 lpmii->cch = 0;
4356 /* copy the text string */
4357 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4358 if( !menu->text ) {
4359 if(lpmii->dwTypeData && lpmii->cch) {
4360 lpmii->cch = 0;
4361 if( unicode)
4362 *((WCHAR *)lpmii->dwTypeData) = 0;
4363 else
4364 *((CHAR *)lpmii->dwTypeData) = 0;
4366 } else {
4367 int len;
4368 if (unicode)
4370 len = strlenW(menu->text);
4371 if(lpmii->dwTypeData && lpmii->cch)
4372 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4374 else
4376 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4377 0, NULL, NULL ) - 1;
4378 if(lpmii->dwTypeData && lpmii->cch)
4379 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4380 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4381 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4383 /* if we've copied a substring we return its length */
4384 if(lpmii->dwTypeData && lpmii->cch)
4385 if (lpmii->cch <= len + 1)
4386 lpmii->cch--;
4387 else
4388 lpmii->cch = len;
4389 else {
4390 /* return length of string */
4391 /* not on Win9x/ME if fType & MFT_BITMAP */
4392 lpmii->cch = len;
4397 if (lpmii->fMask & MIIM_FTYPE)
4398 lpmii->fType = menu->fType & ~MF_POPUP;
4400 if (lpmii->fMask & MIIM_BITMAP)
4401 lpmii->hbmpItem = menu->hbmpItem;
4403 if (lpmii->fMask & MIIM_STATE)
4404 lpmii->fState = menu->fState;
4406 if (lpmii->fMask & MIIM_ID)
4407 lpmii->wID = menu->wID;
4409 if (lpmii->fMask & MIIM_SUBMENU)
4410 lpmii->hSubMenu = menu->hSubMenu;
4411 else {
4412 /* hSubMenu is always cleared
4413 * (not on Win9x/ME ) */
4414 lpmii->hSubMenu = 0;
4417 if (lpmii->fMask & MIIM_CHECKMARKS) {
4418 lpmii->hbmpChecked = menu->hCheckBit;
4419 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4421 if (lpmii->fMask & MIIM_DATA)
4422 lpmii->dwItemData = menu->dwItemData;
4424 return TRUE;
4427 /**********************************************************************
4428 * GetMenuItemInfoA (USER32.@)
4430 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4431 LPMENUITEMINFOA lpmii)
4433 BOOL ret;
4434 MENUITEMINFOA mii;
4435 if( lpmii->cbSize != sizeof( mii) &&
4436 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4437 SetLastError( ERROR_INVALID_PARAMETER);
4438 return FALSE;
4440 memcpy( &mii, lpmii, lpmii->cbSize);
4441 mii.cbSize = sizeof( mii);
4442 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4443 (LPMENUITEMINFOW)&mii, FALSE);
4444 mii.cbSize = lpmii->cbSize;
4445 memcpy( lpmii, &mii, mii.cbSize);
4446 return ret;
4449 /**********************************************************************
4450 * GetMenuItemInfoW (USER32.@)
4452 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4453 LPMENUITEMINFOW lpmii)
4455 BOOL ret;
4456 MENUITEMINFOW mii;
4457 if( lpmii->cbSize != sizeof( mii) &&
4458 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4459 SetLastError( ERROR_INVALID_PARAMETER);
4460 return FALSE;
4462 memcpy( &mii, lpmii, lpmii->cbSize);
4463 mii.cbSize = sizeof( mii);
4464 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4465 mii.cbSize = lpmii->cbSize;
4466 memcpy( lpmii, &mii, mii.cbSize);
4467 return ret;
4471 /* set a menu item text from a ASCII or Unicode string */
4472 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4474 if (!text)
4475 menu->text = NULL;
4476 else if (unicode)
4478 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4479 strcpyW( menu->text, text );
4481 else
4483 LPCSTR str = (LPCSTR)text;
4484 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4485 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4486 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4491 /**********************************************************************
4492 * SetMenuItemInfo_common
4495 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4496 const MENUITEMINFOW *lpmii,
4497 BOOL unicode)
4499 if (!menu) return FALSE;
4501 debug_print_menuitem("SetmenuItemInfo_common from: ", menu, "");
4503 if (lpmii->fMask & MIIM_TYPE ) {
4504 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4505 WARN("invalid combination of fMask bits used\n");
4506 /* this does not happen on Win9x/ME */
4507 SetLastError( ERROR_INVALID_PARAMETER);
4508 return FALSE;
4510 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4511 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4512 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4514 if (IS_STRING_ITEM(menu->fType)) {
4515 HeapFree(GetProcessHeap(), 0, menu->text);
4516 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4517 } else if( (menu->fType) & MFT_BITMAP)
4518 menu->hbmpItem = (HBITMAP)lpmii->dwTypeData;
4521 if (lpmii->fMask & MIIM_FTYPE ) {
4522 if(( lpmii->fType & MFT_BITMAP)) {
4523 SetLastError( ERROR_INVALID_PARAMETER);
4524 return FALSE;
4526 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4527 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4529 if (lpmii->fMask & MIIM_STRING ) {
4530 /* free the string when used */
4531 HeapFree(GetProcessHeap(), 0, menu->text);
4532 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4535 if (lpmii->fMask & MIIM_STATE)
4537 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4538 menu->fState = lpmii->fState;
4541 if (lpmii->fMask & MIIM_ID)
4542 menu->wID = lpmii->wID;
4544 if (lpmii->fMask & MIIM_SUBMENU) {
4545 menu->hSubMenu = lpmii->hSubMenu;
4546 if (menu->hSubMenu) {
4547 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4548 if (subMenu) {
4549 subMenu->wFlags |= MF_POPUP;
4550 menu->fType |= MF_POPUP;
4552 else {
4553 SetLastError( ERROR_INVALID_PARAMETER);
4554 return FALSE;
4557 else
4558 menu->fType &= ~MF_POPUP;
4561 if (lpmii->fMask & MIIM_CHECKMARKS)
4563 if (lpmii->fType & MFT_RADIOCHECK)
4564 menu->fType |= MFT_RADIOCHECK;
4566 menu->hCheckBit = lpmii->hbmpChecked;
4567 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4569 if (lpmii->fMask & MIIM_DATA)
4570 menu->dwItemData = lpmii->dwItemData;
4572 if (lpmii->fMask & MIIM_BITMAP)
4573 menu->hbmpItem = lpmii->hbmpItem;
4575 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4576 menu->fType |= MFT_SEPARATOR;
4578 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4579 return TRUE;
4582 /**********************************************************************
4583 * SetMenuItemInfoA (USER32.@)
4585 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4586 const MENUITEMINFOA *lpmii)
4588 MENUITEMINFOA mii;
4589 if( lpmii->cbSize != sizeof( mii) &&
4590 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4591 SetLastError( ERROR_INVALID_PARAMETER);
4592 return FALSE;
4594 memcpy( &mii, lpmii, lpmii->cbSize);
4595 if( lpmii->cbSize != sizeof( mii)) {
4596 mii.cbSize = sizeof( mii);
4597 mii.hbmpItem = NULL;
4599 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4600 (const MENUITEMINFOW *)&mii, FALSE);
4603 /**********************************************************************
4604 * SetMenuItemInfoW (USER32.@)
4606 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4607 const MENUITEMINFOW *lpmii)
4609 MENUITEMINFOW mii;
4610 if( lpmii->cbSize != sizeof( mii) &&
4611 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4612 SetLastError( ERROR_INVALID_PARAMETER);
4613 return FALSE;
4615 memcpy( &mii, lpmii, lpmii->cbSize);
4616 if( lpmii->cbSize != sizeof( mii)) {
4617 mii.cbSize = sizeof( mii);
4618 mii.hbmpItem = NULL;
4620 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4621 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4624 /**********************************************************************
4625 * SetMenuDefaultItem (USER32.@)
4628 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4630 UINT i;
4631 POPUPMENU *menu;
4632 MENUITEM *item;
4634 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4636 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4638 /* reset all default-item flags */
4639 item = menu->items;
4640 for (i = 0; i < menu->nItems; i++, item++)
4642 item->fState &= ~MFS_DEFAULT;
4645 /* no default item */
4646 if ( -1 == uItem)
4648 return TRUE;
4651 item = menu->items;
4652 if ( bypos )
4654 if ( uItem >= menu->nItems ) return FALSE;
4655 item[uItem].fState |= MFS_DEFAULT;
4656 return TRUE;
4658 else
4660 for (i = 0; i < menu->nItems; i++, item++)
4662 if (item->wID == uItem)
4664 item->fState |= MFS_DEFAULT;
4665 return TRUE;
4670 return FALSE;
4673 /**********************************************************************
4674 * GetMenuDefaultItem (USER32.@)
4676 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4678 POPUPMENU *menu;
4679 MENUITEM * item;
4680 UINT i = 0;
4682 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4684 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4686 /* find default item */
4687 item = menu->items;
4689 /* empty menu */
4690 if (! item) return -1;
4692 while ( !( item->fState & MFS_DEFAULT ) )
4694 i++; item++;
4695 if (i >= menu->nItems ) return -1;
4698 /* default: don't return disabled items */
4699 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4701 /* search rekursiv when needed */
4702 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4704 UINT ret;
4705 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4706 if ( -1 != ret ) return ret;
4708 /* when item not found in submenu, return the popup item */
4710 return ( bypos ) ? i : item->wID;
4715 /**********************************************************************
4716 * InsertMenuItemA (USER32.@)
4718 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4719 const MENUITEMINFOA *lpmii)
4721 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4722 MENUITEMINFOA mii;
4723 if( lpmii->cbSize != sizeof( mii) &&
4724 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4725 SetLastError( ERROR_INVALID_PARAMETER);
4726 return FALSE;
4728 memcpy( &mii, lpmii, lpmii->cbSize);
4729 if( lpmii->cbSize != sizeof( mii)) {
4730 mii.cbSize = sizeof( mii);
4731 mii.hbmpItem = NULL;
4733 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4737 /**********************************************************************
4738 * InsertMenuItemW (USER32.@)
4740 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4741 const MENUITEMINFOW *lpmii)
4743 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4744 MENUITEMINFOW mii;
4745 if( lpmii->cbSize != sizeof( mii) &&
4746 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4747 SetLastError( ERROR_INVALID_PARAMETER);
4748 return FALSE;
4750 memcpy( &mii, lpmii, lpmii->cbSize);
4751 if( lpmii->cbSize != sizeof( mii)) {
4752 mii.cbSize = sizeof( mii);
4753 mii.hbmpItem = NULL;
4755 return SetMenuItemInfo_common(item, &mii, TRUE);
4758 /**********************************************************************
4759 * CheckMenuRadioItem (USER32.@)
4762 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4763 UINT first, UINT last, UINT check,
4764 UINT bypos)
4766 MENUITEM *mifirst, *milast, *micheck;
4767 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4769 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4771 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4772 milast = MENU_FindItem (&mlast, &last, bypos);
4773 micheck = MENU_FindItem (&mcheck, &check, bypos);
4775 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4776 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4777 micheck > milast || micheck < mifirst)
4778 return FALSE;
4780 while (mifirst <= milast)
4782 if (mifirst == micheck)
4784 mifirst->fType |= MFT_RADIOCHECK;
4785 mifirst->fState |= MFS_CHECKED;
4786 } else {
4787 mifirst->fType &= ~MFT_RADIOCHECK;
4788 mifirst->fState &= ~MFS_CHECKED;
4790 mifirst++;
4793 return TRUE;
4797 /**********************************************************************
4798 * GetMenuItemRect (USER32.@)
4800 * ATTENTION: Here, the returned values in rect are the screen
4801 * coordinates of the item just like if the menu was
4802 * always on the upper left side of the application.
4805 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4806 LPRECT rect)
4808 POPUPMENU *itemMenu;
4809 MENUITEM *item;
4810 HWND referenceHwnd;
4812 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4814 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4815 referenceHwnd = hwnd;
4817 if(!hwnd)
4819 itemMenu = MENU_GetMenu(hMenu);
4820 if (itemMenu == NULL)
4821 return FALSE;
4823 if(itemMenu->hWnd == 0)
4824 return FALSE;
4825 referenceHwnd = itemMenu->hWnd;
4828 if ((rect == NULL) || (item == NULL))
4829 return FALSE;
4831 *rect = item->rect;
4833 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4835 return TRUE;
4839 /**********************************************************************
4840 * SetMenuInfo (USER32.@)
4842 * FIXME
4843 * MIM_APPLYTOSUBMENUS
4844 * actually use the items to draw the menu
4846 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4848 POPUPMENU *menu;
4850 TRACE("(%p %p)\n", hMenu, lpmi);
4852 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4855 if (lpmi->fMask & MIM_BACKGROUND)
4856 menu->hbrBack = lpmi->hbrBack;
4858 if (lpmi->fMask & MIM_HELPID)
4859 menu->dwContextHelpID = lpmi->dwContextHelpID;
4861 if (lpmi->fMask & MIM_MAXHEIGHT)
4862 menu->cyMax = lpmi->cyMax;
4864 if (lpmi->fMask & MIM_MENUDATA)
4865 menu->dwMenuData = lpmi->dwMenuData;
4867 if (lpmi->fMask & MIM_STYLE)
4869 menu->dwStyle = lpmi->dwStyle;
4870 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4871 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4872 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4873 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4876 return TRUE;
4878 return FALSE;
4881 /**********************************************************************
4882 * GetMenuInfo (USER32.@)
4884 * NOTES
4885 * win98/NT5.0
4888 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4889 { POPUPMENU *menu;
4891 TRACE("(%p %p)\n", hMenu, lpmi);
4893 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4896 if (lpmi->fMask & MIM_BACKGROUND)
4897 lpmi->hbrBack = menu->hbrBack;
4899 if (lpmi->fMask & MIM_HELPID)
4900 lpmi->dwContextHelpID = menu->dwContextHelpID;
4902 if (lpmi->fMask & MIM_MAXHEIGHT)
4903 lpmi->cyMax = menu->cyMax;
4905 if (lpmi->fMask & MIM_MENUDATA)
4906 lpmi->dwMenuData = menu->dwMenuData;
4908 if (lpmi->fMask & MIM_STYLE)
4909 lpmi->dwStyle = menu->dwStyle;
4911 return TRUE;
4913 return FALSE;
4917 /**********************************************************************
4918 * SetMenuContextHelpId (USER32.@)
4920 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4922 LPPOPUPMENU menu;
4924 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4926 if ((menu = MENU_GetMenu(hMenu)))
4928 menu->dwContextHelpID = dwContextHelpID;
4929 return TRUE;
4931 return FALSE;
4935 /**********************************************************************
4936 * GetMenuContextHelpId (USER32.@)
4938 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4940 LPPOPUPMENU menu;
4942 TRACE("(%p)\n", hMenu);
4944 if ((menu = MENU_GetMenu(hMenu)))
4946 return menu->dwContextHelpID;
4948 return 0;
4951 /**********************************************************************
4952 * MenuItemFromPoint (USER32.@)
4954 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4956 POPUPMENU *menu = MENU_GetMenu(hMenu);
4957 UINT pos;
4959 /*FIXME: Do we have to handle hWnd here? */
4960 if (!menu) return -1;
4961 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4962 return pos;
4966 /**********************************************************************
4967 * translate_accelerator
4969 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4970 BYTE fVirt, WORD key, WORD cmd )
4972 INT mask = 0;
4973 UINT mesg = 0;
4975 if (wParam != key) return FALSE;
4977 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4978 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4979 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4981 if (message == WM_CHAR || message == WM_SYSCHAR)
4983 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4985 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4986 goto found;
4989 else
4991 if(fVirt & FVIRTKEY)
4993 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4994 wParam, 0xff & HIWORD(lParam));
4996 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4997 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4999 else
5001 if (!(lParam & 0x01000000)) /* no special_key */
5003 if ((fVirt & FALT) && (lParam & 0x20000000))
5004 { /* ^^ ALT pressed */
5005 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5006 goto found;
5011 return FALSE;
5013 found:
5014 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5015 mesg = 1;
5016 else
5018 HMENU hMenu, hSubMenu, hSysMenu;
5019 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5021 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5022 hSysMenu = get_win_sys_menu( hWnd );
5024 /* find menu item and ask application to initialize it */
5025 /* 1. in the system menu */
5026 hSubMenu = hSysMenu;
5027 nPos = cmd;
5028 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5030 if (GetCapture())
5031 mesg = 2;
5032 if (!IsWindowEnabled(hWnd))
5033 mesg = 3;
5034 else
5036 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5037 if(hSubMenu != hSysMenu)
5039 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5040 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5041 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5043 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5046 else /* 2. in the window's menu */
5048 hSubMenu = hMenu;
5049 nPos = cmd;
5050 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5052 if (GetCapture())
5053 mesg = 2;
5054 if (!IsWindowEnabled(hWnd))
5055 mesg = 3;
5056 else
5058 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5059 if(hSubMenu != hMenu)
5061 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5062 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5063 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5065 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5070 if (mesg == 0)
5072 if (uSysStat != (UINT)-1)
5074 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5075 mesg=4;
5076 else
5077 mesg=WM_SYSCOMMAND;
5079 else
5081 if (uStat != (UINT)-1)
5083 if (IsIconic(hWnd))
5084 mesg=5;
5085 else
5087 if (uStat & (MF_DISABLED|MF_GRAYED))
5088 mesg=6;
5089 else
5090 mesg=WM_COMMAND;
5093 else
5094 mesg=WM_COMMAND;
5099 if( mesg==WM_COMMAND )
5101 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5102 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5104 else if( mesg==WM_SYSCOMMAND )
5106 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5107 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5109 else
5111 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5112 * #0: unknown (please report!)
5113 * #1: for WM_KEYUP,WM_SYSKEYUP
5114 * #2: mouse is captured
5115 * #3: window is disabled
5116 * #4: it's a disabled system menu option
5117 * #5: it's a menu option, but window is iconic
5118 * #6: it's a menu option, but disabled
5120 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5121 if(mesg==0)
5122 ERR_(accel)(" unknown reason - please report!\n");
5124 return TRUE;
5127 /**********************************************************************
5128 * TranslateAcceleratorA (USER32.@)
5129 * TranslateAccelerator (USER32.@)
5131 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5133 /* YES, Accel16! */
5134 LPACCEL16 lpAccelTbl;
5135 int i;
5136 WPARAM wParam;
5138 if (!hWnd || !msg) return 0;
5140 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5142 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5143 return 0;
5146 wParam = msg->wParam;
5148 switch (msg->message)
5150 case WM_KEYDOWN:
5151 case WM_SYSKEYDOWN:
5152 break;
5154 case WM_CHAR:
5155 case WM_SYSCHAR:
5157 char ch = LOWORD(wParam);
5158 WCHAR wch;
5159 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5160 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5162 break;
5164 default:
5165 return 0;
5168 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5169 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5170 i = 0;
5173 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5174 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5175 return 1;
5176 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5178 return 0;
5181 /**********************************************************************
5182 * TranslateAcceleratorW (USER32.@)
5184 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5186 /* YES, Accel16! */
5187 LPACCEL16 lpAccelTbl;
5188 int i;
5190 if (!hWnd || !msg) return 0;
5192 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5194 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5195 return 0;
5198 switch (msg->message)
5200 case WM_KEYDOWN:
5201 case WM_SYSKEYDOWN:
5202 case WM_CHAR:
5203 case WM_SYSCHAR:
5204 break;
5206 default:
5207 return 0;
5210 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5211 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5212 i = 0;
5215 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5216 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5217 return 1;
5218 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5220 return 0;