Release 0.9.14.
[wine/multimedia.git] / dlls / user / menu.c
bloba9cd87e3ed3307446cbafc52b2048c750ef7c319
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
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", "UNKNOWN BITMAP", "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 fallback_pos = 0;
584 UINT i;
586 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
587 if (wFlags & MF_BYPOSITION)
589 if (*nPos >= menu->nItems) return NULL;
590 return &menu->items[*nPos];
592 else
594 MENUITEM *item = menu->items;
595 for (i = 0; i < menu->nItems; i++, item++)
597 if (item->fType & MF_POPUP)
599 HMENU hsubmenu = item->hSubMenu;
600 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
601 if (subitem)
603 *hmenu = hsubmenu;
604 return subitem;
606 else if (item->wID == *nPos)
608 /* fallback to this item if nothing else found */
609 fallback_pos = i;
610 fallback = item;
613 else if (item->wID == *nPos)
615 *nPos = i;
616 return item;
621 if (fallback)
622 *nPos = fallback_pos;
624 return fallback;
627 /***********************************************************************
628 * MENU_FindSubMenu
630 * Find a Sub menu. Return the position of the submenu, and modifies
631 * *hmenu in case it is found in another sub-menu.
632 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
634 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
636 POPUPMENU *menu;
637 UINT i;
638 MENUITEM *item;
639 if (((*hmenu)==(HMENU)0xffff) ||
640 (!(menu = MENU_GetMenu(*hmenu))))
641 return NO_SELECTED_ITEM;
642 item = menu->items;
643 for (i = 0; i < menu->nItems; i++, item++) {
644 if(!(item->fType & MF_POPUP)) continue;
645 if (item->hSubMenu == hSubTarget) {
646 return i;
648 else {
649 HMENU hsubmenu = item->hSubMenu;
650 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
651 if (pos != NO_SELECTED_ITEM) {
652 *hmenu = hsubmenu;
653 return pos;
657 return NO_SELECTED_ITEM;
660 /***********************************************************************
661 * MENU_FreeItemData
663 static void MENU_FreeItemData( MENUITEM* item )
665 /* delete text */
666 HeapFree( GetProcessHeap(), 0, item->text );
669 /***********************************************************************
670 * MENU_AdjustMenuItemRect
672 * Adjust menu item rectangle according to scrolling state.
674 static void
675 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
677 if (menu->bScrolling)
679 UINT arrow_bitmap_width, arrow_bitmap_height;
680 BITMAP bmp;
682 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
683 arrow_bitmap_width = bmp.bmWidth;
684 arrow_bitmap_height = bmp.bmHeight;
685 rect->top += arrow_bitmap_height - menu->nScrollPos;
686 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
691 /***********************************************************************
692 * MENU_FindItemByCoords
694 * Find the item at the specified coordinates (screen coords). Does
695 * not work for child windows and therefore should not be called for
696 * an arbitrary system menu.
698 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
699 POINT pt, UINT *pos )
701 MENUITEM *item;
702 UINT i;
703 RECT wrect;
704 RECT rect;
706 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
707 pt.x -= wrect.left;pt.y -= wrect.top;
708 item = menu->items;
709 for (i = 0; i < menu->nItems; i++, item++)
711 rect = item->rect;
712 MENU_AdjustMenuItemRect(menu, &rect);
713 if ((pt.x >= rect.left) && (pt.x < rect.right) &&
714 (pt.y >= rect.top) && (pt.y < rect.bottom))
716 if (pos) *pos = i;
717 return item;
720 return NULL;
724 /***********************************************************************
725 * MENU_FindItemByKey
727 * Find the menu item selected by a key press.
728 * Return item id, -1 if none, -2 if we should close the menu.
730 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
731 WCHAR key, BOOL forceMenuChar )
733 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
735 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
737 if (hmenu)
739 POPUPMENU *menu = MENU_GetMenu( hmenu );
740 MENUITEM *item = menu->items;
741 LRESULT menuchar;
743 if( !forceMenuChar )
745 UINT i;
747 for (i = 0; i < menu->nItems; i++, item++)
749 if( item->text)
751 WCHAR *p = item->text - 2;
754 p = strchrW (p + 2, '&');
756 while (p != NULL && p [1] == '&');
757 if (p && (toupperW(p[1]) == toupperW(key))) return i;
761 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
762 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
763 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
764 if (HIWORD(menuchar) == 1) return (UINT)(-2);
766 return (UINT)(-1);
770 /***********************************************************************
771 * MENU_GetBitmapItemSize
773 * Get the size of a bitmap item.
775 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
776 HWND hwndOwner)
778 BITMAP bm;
779 HBITMAP bmp = lpitem->hbmpItem;
781 size->cx = size->cy = 0;
783 /* check if there is a magic menu item associated with this item */
784 switch( (INT_PTR) bmp )
786 case (INT_PTR)HBMMENU_CALLBACK:
788 MEASUREITEMSTRUCT measItem;
789 measItem.CtlType = ODT_MENU;
790 measItem.CtlID = 0;
791 measItem.itemID = lpitem->wID;
792 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
793 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
794 measItem.itemData = lpitem->dwItemData;
795 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
796 size->cx = measItem.itemWidth;
797 size->cy = measItem.itemHeight;
798 return;
800 break;
801 case (INT_PTR)HBMMENU_SYSTEM:
802 if (lpitem->dwItemData)
804 bmp = (HBITMAP)lpitem->dwItemData;
805 break;
807 /* fall through */
808 case (INT_PTR)HBMMENU_MBAR_RESTORE:
809 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
810 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
811 case (INT_PTR)HBMMENU_MBAR_CLOSE:
812 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
813 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
814 size->cy = size->cx;
815 return;
816 case (INT_PTR)HBMMENU_POPUP_CLOSE:
817 case (INT_PTR)HBMMENU_POPUP_RESTORE:
818 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
819 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
820 FIXME("Magic %p not implemented\n", bmp );
821 return;
823 if (GetObjectW(bmp, sizeof(bm), &bm ))
825 size->cx = bm.bmWidth;
826 size->cy = bm.bmHeight;
830 /***********************************************************************
831 * MENU_DrawBitmapItem
833 * Draw a bitmap item.
835 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
836 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
838 BITMAP bm;
839 DWORD rop;
840 HDC hdcMem;
841 HBITMAP bmp;
842 int w = rect->right - rect->left;
843 int h = rect->bottom - rect->top;
844 int bmp_xoffset = 0;
845 int left, top;
846 HBITMAP hbmToDraw = lpitem->hbmpItem;
847 bmp = hbmToDraw;
849 /* Check if there is a magic menu item associated with this item */
850 if (IS_MAGIC_BITMAP(hbmToDraw))
852 UINT flags = 0;
853 RECT r;
855 switch((INT_PTR)hbmToDraw)
857 case (INT_PTR)HBMMENU_SYSTEM:
858 if (lpitem->dwItemData)
860 bmp = (HBITMAP)lpitem->dwItemData;
861 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
863 else
865 static HBITMAP hBmpSysMenu;
867 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
868 bmp = hBmpSysMenu;
869 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
870 /* only use right half of the bitmap */
871 bmp_xoffset = bm.bmWidth / 2;
872 bm.bmWidth -= bmp_xoffset;
874 goto got_bitmap;
875 case (INT_PTR)HBMMENU_MBAR_RESTORE:
876 flags = DFCS_CAPTIONRESTORE;
877 break;
878 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
879 flags = DFCS_CAPTIONMIN;
880 break;
881 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
882 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
883 break;
884 case (INT_PTR)HBMMENU_MBAR_CLOSE:
885 flags = DFCS_CAPTIONCLOSE;
886 break;
887 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
888 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
889 break;
890 case (INT_PTR)HBMMENU_CALLBACK:
892 DRAWITEMSTRUCT drawItem;
893 drawItem.CtlType = ODT_MENU;
894 drawItem.CtlID = 0;
895 drawItem.itemID = lpitem->wID;
896 drawItem.itemAction = odaction;
897 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
898 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
899 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
900 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
901 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
902 drawItem.hwndItem = (HWND)hmenu;
903 drawItem.hDC = hdc;
904 drawItem.itemData = lpitem->dwItemData;
905 drawItem.rcItem = *rect;
906 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
907 return;
909 break;
910 case (INT_PTR)HBMMENU_POPUP_CLOSE:
911 case (INT_PTR)HBMMENU_POPUP_RESTORE:
912 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
913 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
914 default:
915 FIXME("Magic %p not implemented\n", hbmToDraw);
916 return;
918 r = *rect;
919 InflateRect( &r, -1, -1 );
920 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
921 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
922 return;
925 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
927 got_bitmap:
928 hdcMem = CreateCompatibleDC( hdc );
929 SelectObject( hdcMem, bmp );
931 /* handle fontsize > bitmap_height */
932 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
933 left=rect->left;
934 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
935 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
936 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
937 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
938 DeleteDC( hdcMem );
942 /***********************************************************************
943 * MENU_CalcItemSize
945 * Calculate the size of the menu item and store it in lpitem->rect.
947 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
948 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
950 WCHAR *p;
951 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
952 UINT arrow_bitmap_width;
953 BITMAP bm;
954 INT itemheight;
956 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
957 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
958 (menuBar ? " (MenuBar)" : ""));
960 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
961 arrow_bitmap_width = bm.bmWidth;
963 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
964 if( !menucharsize.cx ) {
965 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
966 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
967 * but it is unlikely an application will depend on that */
968 ODitemheight = HIWORD( GetDialogBaseUnits());
971 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
973 if (lpitem->fType & MF_OWNERDRAW)
975 MEASUREITEMSTRUCT mis;
976 mis.CtlType = ODT_MENU;
977 mis.CtlID = 0;
978 mis.itemID = lpitem->wID;
979 mis.itemData = lpitem->dwItemData;
980 mis.itemHeight = ODitemheight;
981 mis.itemWidth = 0;
982 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
983 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
984 * width of a menufont character to the width of an owner-drawn menu.
986 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
987 if (menuBar) {
988 /* under at least win95 you seem to be given a standard
989 height for the menu and the height value is ignored */
990 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
991 } else
992 lpitem->rect.bottom += mis.itemHeight;
994 TRACE("id=%04x size=%ldx%ld\n",
995 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
996 lpitem->rect.bottom-lpitem->rect.top);
997 return;
1000 if (lpitem->fType & MF_SEPARATOR)
1002 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1003 if( !menuBar)
1004 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1005 return;
1008 itemheight = 0;
1009 lpitem->xTab = 0;
1011 if (!menuBar) {
1012 if (lpitem->hbmpItem) {
1013 SIZE size;
1015 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1016 /* Keep the size of the bitmap in callback mode to be able
1017 * to draw it correctly */
1018 lpitem->bmpsize = size;
1019 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1020 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1021 lpitem->rect.right += size.cx + 2;
1022 itemheight = size.cy + 2;
1024 if( !(lppop->dwStyle & MNS_NOCHECK))
1025 lpitem->rect.right += check_bitmap_width;
1026 lpitem->rect.right += 4 + menucharsize.cx;
1027 lpitem->xTab = lpitem->rect.right;
1028 lpitem->rect.right += arrow_bitmap_width;
1029 } else if (lpitem->hbmpItem) { /* menuBar */
1030 SIZE size;
1032 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1033 lpitem->bmpsize = size;
1034 lpitem->rect.right += size.cx;
1035 if( lpitem->text) lpitem->rect.right += 2;
1036 itemheight = size.cy;
1039 /* it must be a text item - unless it's the system menu */
1040 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1041 HFONT hfontOld = NULL;
1042 RECT rc = lpitem->rect;
1043 LONG txtheight, txtwidth;
1045 if ( lpitem->fState & MFS_DEFAULT ) {
1046 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1048 if (menuBar) {
1049 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1050 DT_SINGLELINE|DT_CALCRECT);
1051 lpitem->rect.right += rc.right - rc.left;
1052 itemheight = max( max( itemheight, txtheight),
1053 GetSystemMetrics( SM_CYMENU) - 1);
1054 lpitem->rect.right += 2 * menucharsize.cx;
1055 } else {
1056 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1057 RECT tmprc = rc;
1058 LONG tmpheight;
1059 int n = (int)( p - lpitem->text);
1060 /* Item contains a tab (only meaningful in popup menus) */
1061 /* get text size before the tab */
1062 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1063 DT_SINGLELINE|DT_CALCRECT);
1064 txtwidth = rc.right - rc.left;
1065 p += 1; /* advance past the Tab */
1066 /* get text size after the tab */
1067 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1068 DT_SINGLELINE|DT_CALCRECT);
1069 lpitem->xTab += txtwidth;
1070 txtheight = max( txtheight, tmpheight);
1071 txtwidth += menucharsize.cx + /* space for the tab */
1072 tmprc.right - tmprc.left; /* space for the short cut */
1073 } else {
1074 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1075 DT_SINGLELINE|DT_CALCRECT);
1076 txtwidth = rc.right - rc.left;
1077 lpitem->xTab += txtwidth;
1079 lpitem->rect.right += 2 + txtwidth;
1080 itemheight = max( itemheight,
1081 max( txtheight + 2, menucharsize.cy + 4));
1083 if (hfontOld) SelectObject (hdc, hfontOld);
1084 } else if( menuBar) {
1085 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1087 lpitem->rect.bottom += itemheight;
1088 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1092 /***********************************************************************
1093 * MENU_GetMaxPopupHeight
1095 static UINT
1096 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1098 if (lppop->cyMax)
1099 return lppop->cyMax;
1100 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1104 /***********************************************************************
1105 * MENU_PopupMenuCalcSize
1107 * Calculate the size of a popup menu.
1109 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1111 MENUITEM *lpitem;
1112 HDC hdc;
1113 int start, i;
1114 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1116 lppop->Width = lppop->Height = 0;
1117 if (lppop->nItems == 0) return;
1118 hdc = GetDC( 0 );
1120 SelectObject( hdc, get_menu_font(FALSE));
1122 start = 0;
1123 maxX = 2 + 1;
1125 lppop->maxBmpSize.cx = 0;
1126 lppop->maxBmpSize.cy = 0;
1128 while (start < lppop->nItems)
1130 lpitem = &lppop->items[start];
1131 orgX = maxX;
1132 if( lpitem->fType & MF_MENUBREAK)
1133 orgX += MENU_COL_SPACE;
1134 orgY = MENU_TOP_MARGIN;
1136 maxTab = maxTabWidth = 0;
1137 /* Parse items until column break or end of menu */
1138 for (i = start; i < lppop->nItems; i++, lpitem++)
1140 if ((i != start) &&
1141 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1143 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1145 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1146 maxX = max( maxX, lpitem->rect.right );
1147 orgY = lpitem->rect.bottom;
1148 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1150 maxTab = max( maxTab, lpitem->xTab );
1151 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1155 /* Finish the column (set all items to the largest width found) */
1156 maxX = max( maxX, maxTab + maxTabWidth );
1157 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1159 lpitem->rect.right = maxX;
1160 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1161 lpitem->xTab = maxTab;
1164 lppop->Height = max( lppop->Height, orgY );
1167 lppop->Width = maxX;
1169 /* space for 3d border */
1170 lppop->Height += MENU_BOTTOM_MARGIN;
1171 lppop->Width += 2;
1173 /* Adjust popup height if it exceeds maximum */
1174 maxHeight = MENU_GetMaxPopupHeight(lppop);
1175 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1176 if (lppop->Height >= maxHeight)
1178 lppop->Height = maxHeight;
1179 lppop->bScrolling = TRUE;
1181 else
1183 lppop->bScrolling = FALSE;
1186 ReleaseDC( 0, hdc );
1190 /***********************************************************************
1191 * MENU_MenuBarCalcSize
1193 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1194 * height is off by 1 pixel which causes lengthy window relocations when
1195 * active document window is maximized/restored.
1197 * Calculate the size of the menu bar.
1199 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1200 LPPOPUPMENU lppop, HWND hwndOwner )
1202 MENUITEM *lpitem;
1203 int start, i, orgX, orgY, maxY, helpPos;
1205 if ((lprect == NULL) || (lppop == NULL)) return;
1206 if (lppop->nItems == 0) return;
1207 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1208 lppop->Width = lprect->right - lprect->left;
1209 lppop->Height = 0;
1210 maxY = lprect->top+1;
1211 start = 0;
1212 helpPos = -1;
1213 lppop->maxBmpSize.cx = 0;
1214 lppop->maxBmpSize.cy = 0;
1215 while (start < lppop->nItems)
1217 lpitem = &lppop->items[start];
1218 orgX = lprect->left;
1219 orgY = maxY;
1221 /* Parse items until line break or end of menu */
1222 for (i = start; i < lppop->nItems; i++, lpitem++)
1224 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1225 if ((i != start) &&
1226 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1228 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1229 debug_print_menuitem (" item: ", lpitem, "");
1230 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1232 if (lpitem->rect.right > lprect->right)
1234 if (i != start) break;
1235 else lpitem->rect.right = lprect->right;
1237 maxY = max( maxY, lpitem->rect.bottom );
1238 orgX = lpitem->rect.right;
1241 /* Finish the line (set all items to the largest height found) */
1242 while (start < i) lppop->items[start++].rect.bottom = maxY;
1245 lprect->bottom = maxY;
1246 lppop->Height = lprect->bottom - lprect->top;
1248 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1249 /* the last item (if several lines, only move the last line) */
1250 lpitem = &lppop->items[lppop->nItems-1];
1251 orgY = lpitem->rect.top;
1252 orgX = lprect->right;
1253 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1254 if ( (helpPos==-1) || (helpPos>i) )
1255 break; /* done */
1256 if (lpitem->rect.top != orgY) break; /* Other line */
1257 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1258 lpitem->rect.left += orgX - lpitem->rect.right;
1259 lpitem->rect.right = orgX;
1260 orgX = lpitem->rect.left;
1265 /***********************************************************************
1266 * MENU_DrawScrollArrows
1268 * Draw scroll arrows.
1270 static void
1271 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1273 HDC hdcMem = CreateCompatibleDC(hdc);
1274 HBITMAP hOrigBitmap;
1275 UINT arrow_bitmap_width, arrow_bitmap_height;
1276 BITMAP bmp;
1277 RECT rect;
1279 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1280 arrow_bitmap_width = bmp.bmWidth;
1281 arrow_bitmap_height = bmp.bmHeight;
1284 if (lppop->nScrollPos)
1285 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1286 else
1287 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1288 rect.left = 0;
1289 rect.top = 0;
1290 rect.right = lppop->Width;
1291 rect.bottom = arrow_bitmap_height;
1292 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1293 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1294 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1295 rect.top = lppop->Height - arrow_bitmap_height;
1296 rect.bottom = lppop->Height;
1297 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1298 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1299 SelectObject(hdcMem, get_down_arrow_bitmap());
1300 else
1301 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1302 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1303 lppop->Height - arrow_bitmap_height,
1304 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1305 SelectObject(hdcMem, hOrigBitmap);
1306 DeleteDC(hdcMem);
1310 /***********************************************************************
1311 * draw_popup_arrow
1313 * Draws the popup-menu arrow.
1315 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1316 UINT arrow_bitmap_height)
1318 HDC hdcMem = CreateCompatibleDC( hdc );
1319 HBITMAP hOrigBitmap;
1321 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1322 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1323 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1324 arrow_bitmap_width, arrow_bitmap_height,
1325 hdcMem, 0, 0, SRCCOPY );
1326 SelectObject( hdcMem, hOrigBitmap );
1327 DeleteDC( hdcMem );
1329 /***********************************************************************
1330 * MENU_DrawMenuItem
1332 * Draw a single menu item.
1334 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1335 UINT height, BOOL menuBar, UINT odaction )
1337 RECT rect;
1338 BOOL flat_menu = FALSE;
1339 int bkgnd;
1340 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1341 POPUPMENU *menu = MENU_GetMenu(hmenu);
1342 RECT bmprc;
1344 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1346 if (!menuBar) {
1347 BITMAP bmp;
1348 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1349 arrow_bitmap_width = bmp.bmWidth;
1350 arrow_bitmap_height = bmp.bmHeight;
1353 if (lpitem->fType & MF_SYSMENU)
1355 if( !IsIconic(hwnd) )
1356 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1357 return;
1360 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1361 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1363 /* Setup colors */
1365 if (lpitem->fState & MF_HILITE)
1367 if(menuBar && !flat_menu) {
1368 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1369 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1370 } else {
1371 if(lpitem->fState & MF_GRAYED)
1372 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1373 else
1374 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1375 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1378 else
1380 if (lpitem->fState & MF_GRAYED)
1381 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1382 else
1383 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1384 SetBkColor( hdc, GetSysColor( bkgnd ) );
1387 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1388 rect = lpitem->rect;
1389 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1391 if (lpitem->fType & MF_OWNERDRAW)
1394 ** Experimentation under Windows reveals that an owner-drawn
1395 ** menu is given the rectangle which includes the space it requested
1396 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1397 ** and a popup-menu arrow. This is the value of lpitem->rect.
1398 ** Windows will leave all drawing to the application except for
1399 ** the popup-menu arrow. Windows always draws that itself, after
1400 ** the menu owner has finished drawing.
1402 DRAWITEMSTRUCT dis;
1404 dis.CtlType = ODT_MENU;
1405 dis.CtlID = 0;
1406 dis.itemID = lpitem->wID;
1407 dis.itemData = lpitem->dwItemData;
1408 dis.itemState = 0;
1409 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1410 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1411 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1412 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1413 dis.hwndItem = (HWND)hmenu;
1414 dis.hDC = hdc;
1415 dis.rcItem = rect;
1416 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1417 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1418 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1419 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1420 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1421 /* Draw the popup-menu arrow */
1422 if (lpitem->fType & MF_POPUP)
1423 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1424 arrow_bitmap_height);
1425 return;
1428 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1430 if (lpitem->fState & MF_HILITE)
1432 if (flat_menu)
1434 InflateRect (&rect, -1, -1);
1435 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1436 InflateRect (&rect, 1, 1);
1437 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1439 else
1441 if(menuBar)
1442 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1443 else
1444 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1447 else
1448 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1450 SetBkMode( hdc, TRANSPARENT );
1452 /* vertical separator */
1453 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1455 HPEN oldPen;
1456 RECT rc = rect;
1458 rc.top = 3;
1459 rc.bottom = height - 3;
1460 if (flat_menu)
1462 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1463 MoveToEx( hdc, rc.left, rc.top, NULL );
1464 LineTo( hdc, rc.left, rc.bottom );
1465 SelectObject( hdc, oldPen );
1467 else
1468 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1471 /* horizontal separator */
1472 if (lpitem->fType & MF_SEPARATOR)
1474 HPEN oldPen;
1475 RECT rc = rect;
1477 rc.left++;
1478 rc.right--;
1479 rc.top = ( rc.top + rc.bottom) / 2;
1480 if (flat_menu)
1482 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1483 MoveToEx( hdc, rc.left, rc.top, NULL );
1484 LineTo( hdc, rc.right, rc.top );
1485 SelectObject( hdc, oldPen );
1487 else
1488 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1489 return;
1492 /* helper lines for debugging */
1493 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1494 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1495 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1496 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1499 if (lpitem->hbmpItem) {
1500 /* calculate the bitmap rectangle in coordinates relative
1501 * to the item rectangle */
1502 if( menuBar) {
1503 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1504 bmprc.left = 3;
1505 else
1506 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1507 } else {
1508 bmprc.left = 4;
1509 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1510 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1512 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1513 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1514 bmprc.top = 0;
1515 else
1516 bmprc.top = (lpitem->rect.bottom - lpitem->rect.top -
1517 lpitem->bmpsize.cy) / 2;
1518 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1521 if (!menuBar)
1523 HBITMAP bm;
1524 INT y = rect.top + rect.bottom;
1525 RECT rc = rect;
1526 int checked = FALSE;
1527 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1528 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1529 /* Draw the check mark
1531 * FIXME:
1532 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1534 if( !(menu->dwStyle & MNS_NOCHECK)) {
1535 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1536 lpitem->hUnCheckBit;
1537 if (bm) /* we have a custom bitmap */
1539 HDC hdcMem = CreateCompatibleDC( hdc );
1541 SelectObject( hdcMem, bm );
1542 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1543 check_bitmap_width, check_bitmap_height,
1544 hdcMem, 0, 0, SRCCOPY );
1545 DeleteDC( hdcMem );
1546 checked = TRUE;
1548 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1550 RECT r;
1551 HBITMAP bm = CreateBitmap( check_bitmap_width,
1552 check_bitmap_height, 1, 1, NULL );
1553 HDC hdcMem = CreateCompatibleDC( hdc );
1555 SelectObject( hdcMem, bm );
1556 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1557 DrawFrameControl( hdcMem, &r, DFC_MENU,
1558 (lpitem->fType & MFT_RADIOCHECK) ?
1559 DFCS_MENUBULLET : DFCS_MENUCHECK );
1560 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1561 hdcMem, 0, 0, SRCCOPY );
1562 DeleteDC( hdcMem );
1563 DeleteObject( bm );
1564 checked = TRUE;
1567 if( lpitem->hbmpItem &&
1568 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1569 POINT origorg;
1570 /* some applications make this assumption on the DC's origin */
1571 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1572 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1573 odaction, FALSE);
1574 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1576 /* Draw the popup-menu arrow */
1577 if (lpitem->fType & MF_POPUP)
1578 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1579 arrow_bitmap_height);
1580 rect.left += 4;
1581 if( !(menu->dwStyle & MNS_NOCHECK))
1582 rect.left += check_bitmap_width;
1583 rect.right -= arrow_bitmap_width;
1585 else if( lpitem->hbmpItem)
1586 { /* Draw the bitmap */
1587 POINT origorg;
1589 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1590 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1591 odaction, menuBar);
1592 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1594 /* process text if present */
1595 if (lpitem->text)
1597 register int i;
1598 HFONT hfontOld = 0;
1600 UINT uFormat = (menuBar) ?
1601 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1602 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1604 if( !(menu->dwStyle & MNS_CHECKORBMP))
1605 rect.left += menu->maxBmpSize.cx;
1607 if ( lpitem->fState & MFS_DEFAULT )
1609 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1612 if (menuBar) {
1613 if( lpitem->hbmpItem)
1614 rect.left += lpitem->bmpsize.cx;
1615 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1616 rect.left += menucharsize.cx;
1617 rect.right -= menucharsize.cx;
1620 for (i = 0; lpitem->text[i]; i++)
1621 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1622 break;
1624 if(lpitem->fState & MF_GRAYED)
1626 if (!(lpitem->fState & MF_HILITE) )
1628 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1629 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1630 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1631 --rect.left; --rect.top; --rect.right; --rect.bottom;
1633 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1636 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1638 /* paint the shortcut text */
1639 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1641 if (lpitem->text[i] == '\t')
1643 rect.left = lpitem->xTab;
1644 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1646 else
1648 rect.right = lpitem->xTab;
1649 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1652 if(lpitem->fState & MF_GRAYED)
1654 if (!(lpitem->fState & MF_HILITE) )
1656 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1657 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1658 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1659 --rect.left; --rect.top; --rect.right; --rect.bottom;
1661 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1663 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1666 if (hfontOld)
1667 SelectObject (hdc, hfontOld);
1672 /***********************************************************************
1673 * MENU_DrawPopupMenu
1675 * Paint a popup menu.
1677 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1679 HBRUSH hPrevBrush = 0;
1680 RECT rect;
1682 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1684 GetClientRect( hwnd, &rect );
1686 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1687 && (SelectObject( hdc, get_menu_font(FALSE))))
1689 HPEN hPrevPen;
1691 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1693 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1694 if( hPrevPen )
1696 POPUPMENU *menu;
1697 BOOL flat_menu = FALSE;
1699 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1700 if (flat_menu)
1701 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1702 else
1703 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1705 if( (menu = MENU_GetMenu( hmenu )))
1707 /* draw menu items */
1708 if( menu->nItems)
1710 MENUITEM *item;
1711 UINT u;
1713 item = menu->items;
1714 for( u = menu->nItems; u > 0; u--, item++)
1715 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1716 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1718 /* draw scroll arrows */
1719 if (menu->bScrolling)
1720 MENU_DrawScrollArrows(menu, hdc);
1722 } else
1724 SelectObject( hdc, hPrevBrush );
1729 /***********************************************************************
1730 * MENU_DrawMenuBar
1732 * Paint a menu bar. Returns the height of the menu bar.
1733 * called from [windows/nonclient.c]
1735 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1736 BOOL suppress_draw)
1738 LPPOPUPMENU lppop;
1739 HFONT hfontOld = 0;
1740 HMENU hMenu = GetMenu(hwnd);
1742 lppop = MENU_GetMenu( hMenu );
1743 if (lppop == NULL || lprect == NULL)
1745 return GetSystemMetrics(SM_CYMENU);
1748 if (suppress_draw)
1750 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1752 if (lppop->Height == 0)
1753 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1755 lprect->bottom = lprect->top + lppop->Height;
1757 if (hfontOld) SelectObject( hDC, hfontOld);
1758 return lppop->Height;
1760 else
1761 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1765 /***********************************************************************
1766 * MENU_ShowPopup
1768 * Display a popup menu.
1770 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1771 INT x, INT y, INT xanchor, INT yanchor )
1773 POPUPMENU *menu;
1774 UINT width, height;
1776 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1777 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1779 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1780 if (menu->FocusedItem != NO_SELECTED_ITEM)
1782 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1783 menu->FocusedItem = NO_SELECTED_ITEM;
1786 /* store the owner for DrawItem */
1787 menu->hwndOwner = hwndOwner;
1789 menu->nScrollPos = 0;
1790 MENU_PopupMenuCalcSize( menu, hwndOwner );
1792 /* adjust popup menu pos so that it fits within the desktop */
1794 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1795 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1797 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1799 if( xanchor )
1800 x -= width - xanchor;
1801 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1802 x = GetSystemMetrics(SM_CXSCREEN) - width;
1804 if( x < 0 ) x = 0;
1806 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1808 if( yanchor )
1809 y -= height + yanchor;
1810 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1811 y = GetSystemMetrics(SM_CYSCREEN) - height;
1813 if( y < 0 ) y = 0;
1815 /* NOTE: In Windows, top menu popup is not owned. */
1816 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1817 WS_POPUP, x, y, width, height,
1818 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1819 (LPVOID)hmenu );
1820 if( !menu->hWnd ) return FALSE;
1821 if (!top_popup) top_popup = menu->hWnd;
1823 /* Display the window */
1825 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1826 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1827 UpdateWindow( menu->hWnd );
1828 return TRUE;
1832 /***********************************************************************
1833 * MENU_EnsureMenuItemVisible
1835 static void
1836 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1838 if (lppop->bScrolling)
1840 MENUITEM *item = &lppop->items[wIndex];
1841 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1842 UINT nOldPos = lppop->nScrollPos;
1843 RECT rc;
1844 UINT arrow_bitmap_height;
1845 BITMAP bmp;
1847 GetClientRect(lppop->hWnd, &rc);
1849 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1850 arrow_bitmap_height = bmp.bmHeight;
1852 rc.top += arrow_bitmap_height;
1853 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1855 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1856 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1859 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1860 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1861 MENU_DrawScrollArrows(lppop, hdc);
1863 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1865 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1866 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1867 MENU_DrawScrollArrows(lppop, hdc);
1873 /***********************************************************************
1874 * MENU_SelectItem
1876 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1877 BOOL sendMenuSelect, HMENU topmenu )
1879 LPPOPUPMENU lppop;
1880 HDC hdc;
1882 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1884 lppop = MENU_GetMenu( hmenu );
1885 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1887 if (lppop->FocusedItem == wIndex) return;
1888 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1889 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1890 if (!top_popup) top_popup = lppop->hWnd;
1892 SelectObject( hdc, get_menu_font(FALSE));
1894 /* Clear previous highlighted item */
1895 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1897 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1898 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1899 lppop->Height, !(lppop->wFlags & MF_POPUP),
1900 ODA_SELECT );
1903 /* Highlight new item (if any) */
1904 lppop->FocusedItem = wIndex;
1905 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1907 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1908 lppop->items[wIndex].fState |= MF_HILITE;
1909 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1910 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1911 &lppop->items[wIndex], lppop->Height,
1912 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1914 if (sendMenuSelect)
1916 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1917 SendMessageW( hwndOwner, WM_MENUSELECT,
1918 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1919 ip->fType | ip->fState |
1920 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1923 else if (sendMenuSelect) {
1924 if(topmenu){
1925 int pos;
1926 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1927 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1928 MENUITEM *ip = &ptm->items[pos];
1929 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1930 ip->fType | ip->fState |
1931 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1935 ReleaseDC( lppop->hWnd, hdc );
1939 /***********************************************************************
1940 * MENU_MoveSelection
1942 * Moves currently selected item according to the offset parameter.
1943 * If there is no selection then it should select the last item if
1944 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1946 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1948 INT i;
1949 POPUPMENU *menu;
1951 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1953 menu = MENU_GetMenu( hmenu );
1954 if ((!menu) || (!menu->items)) return;
1956 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1958 if( menu->nItems == 1 ) return; else
1959 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1960 ; i += offset)
1961 if (!(menu->items[i].fType & MF_SEPARATOR))
1963 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1964 return;
1968 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1969 i >= 0 && i < menu->nItems ; i += offset)
1970 if (!(menu->items[i].fType & MF_SEPARATOR))
1972 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1973 return;
1978 /**********************************************************************
1979 * MENU_SetItemData
1981 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1982 * ModifyMenu().
1984 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1985 LPCWSTR str )
1987 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1988 TRACE("flags=%x str=%p\n", flags, str);
1990 if (IS_STRING_ITEM(flags))
1992 LPWSTR prevText = item->text;
1993 if (!str)
1995 flags |= MF_SEPARATOR;
1996 item->text = NULL;
1998 else
2000 LPWSTR text;
2001 /* Item beginning with a backspace is a help item */
2002 if (*str == '\b')
2004 flags |= MF_HELP;
2005 str++;
2007 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2008 return FALSE;
2009 strcpyW( text, str );
2010 item->text = text;
2012 item->hbmpItem = NULL;
2013 HeapFree( GetProcessHeap(), 0, prevText );
2015 else if(( flags & MFT_BITMAP)) {
2016 item->hbmpItem = HBITMAP_32(LOWORD(str));
2017 /* setting bitmap clears text */
2018 HeapFree( GetProcessHeap(), 0, item->text );
2019 item->text = NULL;
2022 if (flags & MF_OWNERDRAW)
2023 item->dwItemData = (DWORD_PTR)str;
2024 else
2025 item->dwItemData = 0;
2027 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2028 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2030 if (flags & MF_POPUP)
2032 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2033 if (menu) menu->wFlags |= MF_POPUP;
2034 else
2036 item->wID = 0;
2037 item->hSubMenu = 0;
2038 item->fType = 0;
2039 item->fState = 0;
2040 return FALSE;
2044 item->wID = id;
2045 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2047 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2048 flags |= MF_POPUP; /* keep popup */
2050 item->fType = flags & TYPE_MASK;
2051 item->fState = (flags & STATE_MASK) &
2052 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
2054 /* Don't call SetRectEmpty here! */
2056 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2057 return TRUE;
2061 /**********************************************************************
2062 * MENU_InsertItem
2064 * Insert (allocate) a new item into a menu.
2066 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2068 MENUITEM *newItems;
2069 POPUPMENU *menu;
2071 if (!(menu = MENU_GetMenu(hMenu)))
2072 return NULL;
2074 /* Find where to insert new item */
2076 if (flags & MF_BYPOSITION) {
2077 if (pos > menu->nItems)
2078 pos = menu->nItems;
2079 } else {
2080 if (!MENU_FindItem( &hMenu, &pos, flags ))
2081 pos = menu->nItems;
2082 else {
2083 if (!(menu = MENU_GetMenu( hMenu )))
2084 return NULL;
2088 /* Create new items array */
2090 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2091 if (!newItems)
2093 WARN("allocation failed\n" );
2094 return NULL;
2096 if (menu->nItems > 0)
2098 /* Copy the old array into the new one */
2099 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2100 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2101 (menu->nItems-pos)*sizeof(MENUITEM) );
2102 HeapFree( GetProcessHeap(), 0, menu->items );
2104 menu->items = newItems;
2105 menu->nItems++;
2106 memset( &newItems[pos], 0, sizeof(*newItems) );
2107 menu->Height = 0; /* force size recalculate */
2108 return &newItems[pos];
2112 /**********************************************************************
2113 * MENU_ParseResource
2115 * Parse a standard menu resource and add items to the menu.
2116 * Return a pointer to the end of the resource.
2118 * NOTE: flags is equivalent to the mtOption field
2120 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2122 WORD flags, id = 0;
2123 LPCSTR str;
2127 flags = GET_WORD(res);
2128 res += sizeof(WORD);
2129 if (!(flags & MF_POPUP))
2131 id = GET_WORD(res);
2132 res += sizeof(WORD);
2134 str = res;
2135 if (!unicode) res += strlen(str) + 1;
2136 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2137 if (flags & MF_POPUP)
2139 HMENU hSubMenu = CreatePopupMenu();
2140 if (!hSubMenu) return NULL;
2141 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2142 return NULL;
2143 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2144 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2146 else /* Not a popup */
2148 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2149 else AppendMenuW( hMenu, flags, id,
2150 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2152 } while (!(flags & MF_END));
2153 return res;
2157 /**********************************************************************
2158 * MENUEX_ParseResource
2160 * Parse an extended menu resource and add items to the menu.
2161 * Return a pointer to the end of the resource.
2163 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2165 WORD resinfo;
2166 do {
2167 MENUITEMINFOW mii;
2169 mii.cbSize = sizeof(mii);
2170 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2171 mii.fType = GET_DWORD(res);
2172 res += sizeof(DWORD);
2173 mii.fState = GET_DWORD(res);
2174 res += sizeof(DWORD);
2175 mii.wID = GET_DWORD(res);
2176 res += sizeof(DWORD);
2177 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2178 res += sizeof(WORD);
2179 /* Align the text on a word boundary. */
2180 res += (~((UINT_PTR)res - 1)) & 1;
2181 mii.dwTypeData = (LPWSTR) res;
2182 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2183 /* Align the following fields on a dword boundary. */
2184 res += (~((UINT_PTR)res - 1)) & 3;
2186 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2187 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2189 if (resinfo & 1) { /* Pop-up? */
2190 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2191 res += sizeof(DWORD);
2192 mii.hSubMenu = CreatePopupMenu();
2193 if (!mii.hSubMenu)
2194 return NULL;
2195 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2196 DestroyMenu(mii.hSubMenu);
2197 return NULL;
2199 mii.fMask |= MIIM_SUBMENU;
2200 mii.fType |= MF_POPUP;
2202 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2204 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2205 mii.wID, mii.fType);
2206 mii.fType |= MF_SEPARATOR;
2208 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2209 } while (!(resinfo & MF_END));
2210 return res;
2214 /***********************************************************************
2215 * MENU_GetSubPopup
2217 * Return the handle of the selected sub-popup menu (if any).
2219 static HMENU MENU_GetSubPopup( HMENU hmenu )
2221 POPUPMENU *menu;
2222 MENUITEM *item;
2224 menu = MENU_GetMenu( hmenu );
2226 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2228 item = &menu->items[menu->FocusedItem];
2229 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2230 return item->hSubMenu;
2231 return 0;
2235 /***********************************************************************
2236 * MENU_HideSubPopups
2238 * Hide the sub-popup menus of this menu.
2240 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2241 BOOL sendMenuSelect )
2243 POPUPMENU *menu = MENU_GetMenu( hmenu );
2245 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2247 if (menu && top_popup)
2249 HMENU hsubmenu;
2250 POPUPMENU *submenu;
2251 MENUITEM *item;
2253 if (menu->FocusedItem != NO_SELECTED_ITEM)
2255 item = &menu->items[menu->FocusedItem];
2256 if (!(item->fType & MF_POPUP) ||
2257 !(item->fState & MF_MOUSESELECT)) return;
2258 item->fState &= ~MF_MOUSESELECT;
2259 hsubmenu = item->hSubMenu;
2260 } else return;
2262 submenu = MENU_GetMenu( hsubmenu );
2263 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2264 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2265 DestroyWindow( submenu->hWnd );
2266 submenu->hWnd = 0;
2271 /***********************************************************************
2272 * MENU_ShowSubPopup
2274 * Display the sub-menu of the selected item of this menu.
2275 * Return the handle of the submenu, or hmenu if no submenu to display.
2277 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2278 BOOL selectFirst, UINT wFlags )
2280 RECT rect;
2281 POPUPMENU *menu;
2282 MENUITEM *item;
2283 HDC hdc;
2285 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2287 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2289 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2291 item = &menu->items[menu->FocusedItem];
2292 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2293 return hmenu;
2295 /* message must be sent before using item,
2296 because nearly everything may be changed by the application ! */
2298 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2299 if (!(wFlags & TPM_NONOTIFY))
2300 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2301 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2303 item = &menu->items[menu->FocusedItem];
2304 rect = item->rect;
2306 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2307 if (!(item->fState & MF_HILITE))
2309 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2310 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2312 SelectObject( hdc, get_menu_font(FALSE));
2314 item->fState |= MF_HILITE;
2315 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2316 ReleaseDC( menu->hWnd, hdc );
2318 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2319 item->rect = rect;
2321 item->fState |= MF_MOUSESELECT;
2323 if (IS_SYSTEM_MENU(menu))
2325 MENU_InitSysMenuPopup(item->hSubMenu,
2326 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2327 GetClassLongW( menu->hWnd, GCL_STYLE));
2329 NC_GetSysPopupPos( menu->hWnd, &rect );
2330 rect.top = rect.bottom;
2331 rect.right = GetSystemMetrics(SM_CXSIZE);
2332 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2334 else
2336 GetWindowRect( menu->hWnd, &rect );
2337 if (menu->wFlags & MF_POPUP)
2339 RECT rc = item->rect;
2341 MENU_AdjustMenuItemRect(menu, &rc);
2342 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2343 rect.top += rc.top;
2344 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2345 rect.bottom = rc.top - rc.bottom;
2347 else
2349 rect.left += item->rect.left;
2350 rect.top += item->rect.bottom;
2351 rect.right = item->rect.right - item->rect.left;
2352 rect.bottom = item->rect.bottom - item->rect.top;
2356 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2357 rect.left, rect.top, rect.right, rect.bottom );
2358 if (selectFirst)
2359 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2360 return item->hSubMenu;
2365 /**********************************************************************
2366 * MENU_IsMenuActive
2368 HWND MENU_IsMenuActive(void)
2370 return top_popup;
2373 /***********************************************************************
2374 * MENU_PtMenu
2376 * Walks menu chain trying to find a menu pt maps to.
2378 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2380 POPUPMENU *menu = MENU_GetMenu( hMenu );
2381 UINT item = menu->FocusedItem;
2382 HMENU ret;
2384 /* try subpopup first (if any) */
2385 ret = (item != NO_SELECTED_ITEM &&
2386 (menu->items[item].fType & MF_POPUP) &&
2387 (menu->items[item].fState & MF_MOUSESELECT))
2388 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2390 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2392 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2393 if( menu->wFlags & MF_POPUP )
2395 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2397 else if (ht == HTSYSMENU)
2398 ret = get_win_sys_menu( menu->hWnd );
2399 else if (ht == HTMENU)
2400 ret = GetMenu( menu->hWnd );
2402 return ret;
2405 /***********************************************************************
2406 * MENU_ExecFocusedItem
2408 * Execute a menu item (for instance when user pressed Enter).
2409 * Return the wID of the executed item. Otherwise, -1 indicating
2410 * that no menu item was executed, -2 if a popup is shown;
2411 * Have to receive the flags for the TrackPopupMenu options to avoid
2412 * sending unwanted message.
2415 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2417 MENUITEM *item;
2418 POPUPMENU *menu = MENU_GetMenu( hMenu );
2420 TRACE("%p hmenu=%p\n", pmt, hMenu);
2422 if (!menu || !menu->nItems ||
2423 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2425 item = &menu->items[menu->FocusedItem];
2427 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2429 if (!(item->fType & MF_POPUP))
2431 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2433 /* If TPM_RETURNCMD is set you return the id, but
2434 do not send a message to the owner */
2435 if(!(wFlags & TPM_RETURNCMD))
2437 if( menu->wFlags & MF_SYSMENU )
2438 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2439 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2440 else
2441 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2443 return item->wID;
2446 else
2448 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2449 return -2;
2452 return -1;
2455 /***********************************************************************
2456 * MENU_SwitchTracking
2458 * Helper function for menu navigation routines.
2460 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2462 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2463 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2465 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2467 if( pmt->hTopMenu != hPtMenu &&
2468 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2470 /* both are top level menus (system and menu-bar) */
2471 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2472 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2473 pmt->hTopMenu = hPtMenu;
2475 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2476 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2480 /***********************************************************************
2481 * MENU_ButtonDown
2483 * Return TRUE if we can go on with menu tracking.
2485 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2487 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2489 if (hPtMenu)
2491 UINT id = 0;
2492 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2493 MENUITEM *item;
2495 if( IS_SYSTEM_MENU(ptmenu) )
2496 item = ptmenu->items;
2497 else
2498 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2500 if( item )
2502 if( ptmenu->FocusedItem != id )
2503 MENU_SwitchTracking( pmt, hPtMenu, id );
2505 /* If the popup menu is not already "popped" */
2506 if(!(item->fState & MF_MOUSESELECT ))
2508 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2511 return TRUE;
2513 /* Else the click was on the menu bar, finish the tracking */
2515 return FALSE;
2518 /***********************************************************************
2519 * MENU_ButtonUp
2521 * Return the value of MENU_ExecFocusedItem if
2522 * the selected item was not a popup. Else open the popup.
2523 * A -1 return value indicates that we go on with menu tracking.
2526 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2528 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2530 if (hPtMenu)
2532 UINT id = 0;
2533 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2534 MENUITEM *item;
2536 if( IS_SYSTEM_MENU(ptmenu) )
2537 item = ptmenu->items;
2538 else
2539 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2541 if( item && (ptmenu->FocusedItem == id ))
2543 if( !(item->fType & MF_POPUP) )
2545 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2546 return (executedMenuId < 0) ? -1 : executedMenuId;
2549 /* If we are dealing with the top-level menu */
2550 /* and this is a click on an already "popped" item: */
2551 /* Stop the menu tracking and close the opened submenus */
2552 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2553 return 0;
2555 ptmenu->bTimeToHide = TRUE;
2557 return -1;
2561 /***********************************************************************
2562 * MENU_MouseMove
2564 * Return TRUE if we can go on with menu tracking.
2566 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2568 UINT id = NO_SELECTED_ITEM;
2569 POPUPMENU *ptmenu = NULL;
2571 if( hPtMenu )
2573 ptmenu = MENU_GetMenu( hPtMenu );
2574 if( IS_SYSTEM_MENU(ptmenu) )
2575 id = 0;
2576 else
2577 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2580 if( id == NO_SELECTED_ITEM )
2582 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2583 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2586 else if( ptmenu->FocusedItem != id )
2588 MENU_SwitchTracking( pmt, hPtMenu, id );
2589 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2591 return TRUE;
2595 /***********************************************************************
2596 * MENU_SetCapture
2598 static void MENU_SetCapture( HWND hwnd )
2600 HWND previous = 0;
2602 SERVER_START_REQ( set_capture_window )
2604 req->handle = hwnd;
2605 req->flags = CAPTURE_MENU;
2606 if (!wine_server_call_err( req ))
2608 previous = reply->previous;
2609 hwnd = reply->full_handle;
2612 SERVER_END_REQ;
2614 if (previous && previous != hwnd)
2615 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2619 /***********************************************************************
2620 * MENU_DoNextMenu
2622 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2624 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2626 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2628 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2629 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2631 MDINEXTMENU next_menu;
2632 HMENU hNewMenu;
2633 HWND hNewWnd;
2634 UINT id = 0;
2636 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2637 next_menu.hmenuNext = 0;
2638 next_menu.hwndNext = 0;
2639 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2641 TRACE("%p [%p] -> %p [%p]\n",
2642 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2644 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2646 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2647 hNewWnd = pmt->hOwnerWnd;
2648 if( IS_SYSTEM_MENU(menu) )
2650 /* switch to the menu bar */
2652 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2654 if( vk == VK_LEFT )
2656 menu = MENU_GetMenu( hNewMenu );
2657 id = menu->nItems - 1;
2660 else if (style & WS_SYSMENU )
2662 /* switch to the system menu */
2663 hNewMenu = get_win_sys_menu( hNewWnd );
2665 else return FALSE;
2667 else /* application returned a new menu to switch to */
2669 hNewMenu = next_menu.hmenuNext;
2670 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2672 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2674 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2676 if (style & WS_SYSMENU &&
2677 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2679 /* get the real system menu */
2680 hNewMenu = get_win_sys_menu(hNewWnd);
2682 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2684 /* FIXME: Not sure what to do here;
2685 * perhaps try to track hNewMenu as a popup? */
2687 TRACE(" -- got confused.\n");
2688 return FALSE;
2691 else return FALSE;
2694 if( hNewMenu != pmt->hTopMenu )
2696 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2697 FALSE, 0 );
2698 if( pmt->hCurrentMenu != pmt->hTopMenu )
2699 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2702 if( hNewWnd != pmt->hOwnerWnd )
2704 pmt->hOwnerWnd = hNewWnd;
2705 MENU_SetCapture( pmt->hOwnerWnd );
2708 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2709 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2711 return TRUE;
2713 return FALSE;
2716 /***********************************************************************
2717 * MENU_SuspendPopup
2719 * The idea is not to show the popup if the next input message is
2720 * going to hide it anyway.
2722 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2724 MSG msg;
2726 msg.hwnd = pmt->hOwnerWnd;
2728 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2729 pmt->trackFlags |= TF_SKIPREMOVE;
2731 switch( uMsg )
2733 case WM_KEYDOWN:
2734 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2735 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2737 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2738 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2739 if( msg.message == WM_KEYDOWN &&
2740 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2742 pmt->trackFlags |= TF_SUSPENDPOPUP;
2743 return TRUE;
2746 break;
2749 /* failures go through this */
2750 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2751 return FALSE;
2754 /***********************************************************************
2755 * MENU_KeyEscape
2757 * Handle a VK_ESCAPE key event in a menu.
2759 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2761 BOOL bEndMenu = TRUE;
2763 if (pmt->hCurrentMenu != pmt->hTopMenu)
2765 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2767 if (menu->wFlags & MF_POPUP)
2769 HMENU hmenutmp, hmenuprev;
2771 hmenuprev = hmenutmp = pmt->hTopMenu;
2773 /* close topmost popup */
2774 while (hmenutmp != pmt->hCurrentMenu)
2776 hmenuprev = hmenutmp;
2777 hmenutmp = MENU_GetSubPopup( hmenuprev );
2780 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2781 pmt->hCurrentMenu = hmenuprev;
2782 bEndMenu = FALSE;
2786 return bEndMenu;
2789 /***********************************************************************
2790 * MENU_KeyLeft
2792 * Handle a VK_LEFT key event in a menu.
2794 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2796 POPUPMENU *menu;
2797 HMENU hmenutmp, hmenuprev;
2798 UINT prevcol;
2800 hmenuprev = hmenutmp = pmt->hTopMenu;
2801 menu = MENU_GetMenu( hmenutmp );
2803 /* Try to move 1 column left (if possible) */
2804 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2805 NO_SELECTED_ITEM ) {
2807 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2808 prevcol, TRUE, 0 );
2809 return;
2812 /* close topmost popup */
2813 while (hmenutmp != pmt->hCurrentMenu)
2815 hmenuprev = hmenutmp;
2816 hmenutmp = MENU_GetSubPopup( hmenuprev );
2819 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2820 pmt->hCurrentMenu = hmenuprev;
2822 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2824 /* move menu bar selection if no more popups are left */
2826 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2827 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2829 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2831 /* A sublevel menu was displayed - display the next one
2832 * unless there is another displacement coming up */
2834 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2835 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2836 pmt->hTopMenu, TRUE, wFlags);
2842 /***********************************************************************
2843 * MENU_KeyRight
2845 * Handle a VK_RIGHT key event in a menu.
2847 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2849 HMENU hmenutmp;
2850 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2851 UINT nextcol;
2853 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2854 pmt->hCurrentMenu,
2855 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2856 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2858 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2860 /* If already displaying a popup, try to display sub-popup */
2862 hmenutmp = pmt->hCurrentMenu;
2863 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2865 /* if subpopup was displayed then we are done */
2866 if (hmenutmp != pmt->hCurrentMenu) return;
2869 /* Check to see if there's another column */
2870 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2871 NO_SELECTED_ITEM ) {
2872 TRACE("Going to %d.\n", nextcol );
2873 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2874 nextcol, TRUE, 0 );
2875 return;
2878 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2880 if( pmt->hCurrentMenu != pmt->hTopMenu )
2882 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2883 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2884 } else hmenutmp = 0;
2886 /* try to move to the next item */
2887 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2888 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2890 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2891 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2892 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2893 pmt->hTopMenu, TRUE, wFlags);
2897 /***********************************************************************
2898 * MENU_TrackMenu
2900 * Menu tracking code.
2902 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2903 HWND hwnd, const RECT *lprect )
2905 MSG msg;
2906 POPUPMENU *menu;
2907 BOOL fRemove;
2908 INT executedMenuId = -1;
2909 MTRACKER mt;
2910 BOOL enterIdleSent = FALSE;
2912 mt.trackFlags = 0;
2913 mt.hCurrentMenu = hmenu;
2914 mt.hTopMenu = hmenu;
2915 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2916 mt.pt.x = x;
2917 mt.pt.y = y;
2919 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2920 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2922 fEndMenu = FALSE;
2923 if (!(menu = MENU_GetMenu( hmenu )))
2925 WARN("Invalid menu handle %p\n", hmenu);
2926 SetLastError(ERROR_INVALID_MENU_HANDLE);
2927 return FALSE;
2930 if (wFlags & TPM_BUTTONDOWN)
2932 /* Get the result in order to start the tracking or not */
2933 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2934 fEndMenu = !fRemove;
2937 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2939 MENU_SetCapture( mt.hOwnerWnd );
2941 while (!fEndMenu)
2943 menu = MENU_GetMenu( mt.hCurrentMenu );
2944 if (!menu) /* sometimes happens if I do a window manager close */
2945 break;
2947 /* we have to keep the message in the queue until it's
2948 * clear that menu loop is not over yet. */
2950 for (;;)
2952 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2954 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2955 /* remove the message from the queue */
2956 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2958 else
2960 if (!enterIdleSent)
2962 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2963 enterIdleSent = TRUE;
2964 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2966 WaitMessage();
2970 /* check if EndMenu() tried to cancel us, by posting this message */
2971 if(msg.message == WM_CANCELMODE)
2973 /* we are now out of the loop */
2974 fEndMenu = TRUE;
2976 /* remove the message from the queue */
2977 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2979 /* break out of internal loop, ala ESCAPE */
2980 break;
2983 TranslateMessage( &msg );
2984 mt.pt = msg.pt;
2986 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2987 enterIdleSent=FALSE;
2989 fRemove = FALSE;
2990 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2993 * Use the mouse coordinates in lParam instead of those in the MSG
2994 * struct to properly handle synthetic messages. They are already
2995 * in screen coordinates.
2997 mt.pt.x = (short)LOWORD(msg.lParam);
2998 mt.pt.y = (short)HIWORD(msg.lParam);
3000 /* Find a menu for this mouse event */
3001 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3003 switch(msg.message)
3005 /* no WM_NC... messages in captured state */
3007 case WM_RBUTTONDBLCLK:
3008 case WM_RBUTTONDOWN:
3009 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3010 /* fall through */
3011 case WM_LBUTTONDBLCLK:
3012 case WM_LBUTTONDOWN:
3013 /* If the message belongs to the menu, removes it from the queue */
3014 /* Else, end menu tracking */
3015 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3016 fEndMenu = !fRemove;
3017 break;
3019 case WM_RBUTTONUP:
3020 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3021 /* fall through */
3022 case WM_LBUTTONUP:
3023 /* Check if a menu was selected by the mouse */
3024 if (hmenu)
3026 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3028 /* End the loop if executedMenuId is an item ID */
3029 /* or if the job was done (executedMenuId = 0). */
3030 fEndMenu = fRemove = (executedMenuId != -1);
3032 /* No menu was selected by the mouse */
3033 /* if the function was called by TrackPopupMenu, continue
3034 with the menu tracking. If not, stop it */
3035 else
3036 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3038 break;
3040 case WM_MOUSEMOVE:
3041 /* the selected menu item must be changed every time */
3042 /* the mouse moves. */
3044 if (hmenu)
3045 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3047 } /* switch(msg.message) - mouse */
3049 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3051 fRemove = TRUE; /* Keyboard messages are always removed */
3052 switch(msg.message)
3054 case WM_KEYDOWN:
3055 case WM_SYSKEYDOWN:
3056 switch(msg.wParam)
3058 case VK_MENU:
3059 fEndMenu = TRUE;
3060 break;
3062 case VK_HOME:
3063 case VK_END:
3064 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3065 NO_SELECTED_ITEM, FALSE, 0 );
3066 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3067 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3068 break;
3070 case VK_UP:
3071 case VK_DOWN: /* If on menu bar, pull-down the menu */
3073 menu = MENU_GetMenu( mt.hCurrentMenu );
3074 if (!(menu->wFlags & MF_POPUP))
3075 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3076 else /* otherwise try to move selection */
3077 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3078 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3079 break;
3081 case VK_LEFT:
3082 MENU_KeyLeft( &mt, wFlags );
3083 break;
3085 case VK_RIGHT:
3086 MENU_KeyRight( &mt, wFlags );
3087 break;
3089 case VK_ESCAPE:
3090 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3091 break;
3093 case VK_F1:
3095 HELPINFO hi;
3096 hi.cbSize = sizeof(HELPINFO);
3097 hi.iContextType = HELPINFO_MENUITEM;
3098 if (menu->FocusedItem == NO_SELECTED_ITEM)
3099 hi.iCtrlId = 0;
3100 else
3101 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3102 hi.hItemHandle = hmenu;
3103 hi.dwContextId = menu->dwContextHelpID;
3104 hi.MousePos = msg.pt;
3105 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3106 break;
3109 default:
3110 break;
3112 break; /* WM_KEYDOWN */
3114 case WM_CHAR:
3115 case WM_SYSCHAR:
3117 UINT pos;
3119 if (msg.wParam == '\r' || msg.wParam == ' ')
3121 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3122 fEndMenu = (executedMenuId != -2);
3124 break;
3127 /* Hack to avoid control chars. */
3128 /* We will find a better way real soon... */
3129 if (msg.wParam < 32) break;
3131 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3132 LOWORD(msg.wParam), FALSE );
3133 if (pos == (UINT)-2) fEndMenu = TRUE;
3134 else if (pos == (UINT)-1) MessageBeep(0);
3135 else
3137 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3138 TRUE, 0 );
3139 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3140 fEndMenu = (executedMenuId != -2);
3143 break;
3144 } /* switch(msg.message) - kbd */
3146 else
3148 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3149 DispatchMessageW( &msg );
3150 continue;
3153 if (!fEndMenu) fRemove = TRUE;
3155 /* finally remove message from the queue */
3157 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3158 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3159 else mt.trackFlags &= ~TF_SKIPREMOVE;
3162 MENU_SetCapture(0); /* release the capture */
3164 /* If dropdown is still painted and the close box is clicked on
3165 then the menu will be destroyed as part of the DispatchMessage above.
3166 This will then invalidate the menu handle in mt.hTopMenu. We should
3167 check for this first. */
3168 if( IsMenu( mt.hTopMenu ) )
3170 menu = MENU_GetMenu( mt.hTopMenu );
3172 if( IsWindow( mt.hOwnerWnd ) )
3174 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3176 if (menu && (menu->wFlags & MF_POPUP))
3178 DestroyWindow( menu->hWnd );
3179 menu->hWnd = 0;
3181 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3182 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3185 /* Reset the variable for hiding menu */
3186 if( menu ) menu->bTimeToHide = FALSE;
3189 /* The return value is only used by TrackPopupMenu */
3190 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3191 if (executedMenuId < 0) executedMenuId = 0;
3192 return executedMenuId;
3195 /***********************************************************************
3196 * MENU_InitTracking
3198 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3200 POPUPMENU *menu;
3202 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3204 HideCaret(0);
3206 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3207 if (!(wFlags & TPM_NONOTIFY))
3208 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3210 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3212 if (!(wFlags & TPM_NONOTIFY))
3214 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3215 /* If an app changed/recreated menu bar entries in WM_INITMENU
3216 * menu sizes will be recalculated once the menu created/shown.
3220 /* This makes the menus of applications built with Delphi work.
3221 * It also enables menus to be displayed in more than one window,
3222 * but there are some bugs left that need to be fixed in this case.
3224 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3226 return TRUE;
3228 /***********************************************************************
3229 * MENU_ExitTracking
3231 static BOOL MENU_ExitTracking(HWND hWnd)
3233 TRACE("hwnd=%p\n", hWnd);
3235 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3236 ShowCaret(0);
3237 top_popup = 0;
3238 return TRUE;
3241 /***********************************************************************
3242 * MENU_TrackMouseMenuBar
3244 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3246 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3248 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3249 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3251 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3253 if (IsMenu(hMenu))
3255 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3256 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3257 MENU_ExitTracking(hWnd);
3262 /***********************************************************************
3263 * MENU_TrackKbdMenuBar
3265 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3267 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3269 UINT uItem = NO_SELECTED_ITEM;
3270 HMENU hTrackMenu;
3271 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3273 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3275 /* find window that has a menu */
3277 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3278 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3280 /* check if we have to track a system menu */
3282 hTrackMenu = GetMenu( hwnd );
3283 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3285 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3286 hTrackMenu = get_win_sys_menu( hwnd );
3287 uItem = 0;
3288 wParam |= HTSYSMENU; /* prevent item lookup */
3291 if (!IsMenu( hTrackMenu )) return;
3293 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3295 if( wChar && wChar != ' ' )
3297 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3298 if ( uItem >= (UINT)(-2) )
3300 if( uItem == (UINT)(-1) ) MessageBeep(0);
3301 /* schedule end of menu tracking */
3302 wFlags |= TF_ENDMENU;
3303 goto track_menu;
3307 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3309 if (wParam & HTSYSMENU)
3311 /* prevent sysmenu activation for managed windows on Alt down/up */
3312 if (GetPropA( hwnd, "__wine_x11_managed" ))
3313 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3315 else
3317 if( uItem == NO_SELECTED_ITEM )
3318 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3319 else
3320 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3323 track_menu:
3324 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3325 MENU_ExitTracking( hwnd );
3329 /**********************************************************************
3330 * TrackPopupMenu (USER32.@)
3332 * Like the win32 API, the function return the command ID only if the
3333 * flag TPM_RETURNCMD is on.
3336 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3337 INT nReserved, HWND hWnd, const RECT *lpRect )
3339 BOOL ret = FALSE;
3341 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3343 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3344 if (!(wFlags & TPM_NONOTIFY))
3345 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3347 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3348 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3349 MENU_ExitTracking(hWnd);
3351 return ret;
3354 /**********************************************************************
3355 * TrackPopupMenuEx (USER32.@)
3357 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3358 HWND hWnd, LPTPMPARAMS lpTpm )
3360 FIXME("not fully implemented\n" );
3361 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3362 lpTpm ? &lpTpm->rcExclude : NULL );
3365 /***********************************************************************
3366 * PopupMenuWndProc
3368 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3370 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3372 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3374 switch(message)
3376 case WM_CREATE:
3378 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3379 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3380 return 0;
3383 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3384 return MA_NOACTIVATE;
3386 case WM_PAINT:
3388 PAINTSTRUCT ps;
3389 BeginPaint( hwnd, &ps );
3390 MENU_DrawPopupMenu( hwnd, ps.hdc,
3391 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3392 EndPaint( hwnd, &ps );
3393 return 0;
3395 case WM_ERASEBKGND:
3396 return 1;
3398 case WM_DESTROY:
3399 /* zero out global pointer in case resident popup window was destroyed. */
3400 if (hwnd == top_popup) top_popup = 0;
3401 break;
3403 case WM_SHOWWINDOW:
3405 if( wParam )
3407 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3409 else
3410 SetWindowLongPtrW( hwnd, 0, 0 );
3411 break;
3413 case MM_SETMENUHANDLE:
3414 SetWindowLongPtrW( hwnd, 0, wParam );
3415 break;
3417 case MM_GETMENUHANDLE:
3418 return GetWindowLongPtrW( hwnd, 0 );
3420 default:
3421 return DefWindowProcW( hwnd, message, wParam, lParam );
3423 return 0;
3427 /***********************************************************************
3428 * MENU_GetMenuBarHeight
3430 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3432 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3433 INT orgX, INT orgY )
3435 HDC hdc;
3436 RECT rectBar;
3437 LPPOPUPMENU lppop;
3439 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3441 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3443 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3444 SelectObject( hdc, get_menu_font(FALSE));
3445 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3446 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3447 ReleaseDC( hwnd, hdc );
3448 return lppop->Height;
3452 /*******************************************************************
3453 * ChangeMenuA (USER32.@)
3455 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3456 UINT id, UINT flags )
3458 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3459 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3460 id, data );
3461 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3462 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3463 id, data );
3464 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3465 flags & MF_BYPOSITION ? pos : id,
3466 flags & ~MF_REMOVE );
3467 /* Default: MF_INSERT */
3468 return InsertMenuA( hMenu, pos, flags, id, data );
3472 /*******************************************************************
3473 * ChangeMenuW (USER32.@)
3475 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3476 UINT id, UINT flags )
3478 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3479 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3480 id, data );
3481 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3482 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3483 id, data );
3484 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3485 flags & MF_BYPOSITION ? pos : id,
3486 flags & ~MF_REMOVE );
3487 /* Default: MF_INSERT */
3488 return InsertMenuW( hMenu, pos, flags, id, data );
3492 /*******************************************************************
3493 * CheckMenuItem (USER32.@)
3495 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3497 MENUITEM *item;
3498 DWORD ret;
3500 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3501 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3502 ret = item->fState & MF_CHECKED;
3503 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3504 else item->fState &= ~MF_CHECKED;
3505 return ret;
3509 /**********************************************************************
3510 * EnableMenuItem (USER32.@)
3512 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3514 UINT oldflags;
3515 MENUITEM *item;
3516 POPUPMENU *menu;
3518 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3520 /* Get the Popupmenu to access the owner menu */
3521 if (!(menu = MENU_GetMenu(hMenu)))
3522 return (UINT)-1;
3524 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3525 return (UINT)-1;
3527 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3528 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3530 /* If the close item in the system menu change update the close button */
3531 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3533 if (menu->hSysMenuOwner != 0)
3535 RECT rc;
3536 POPUPMENU* parentMenu;
3538 /* Get the parent menu to access*/
3539 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3540 return (UINT)-1;
3542 /* Refresh the frame to reflect the change */
3543 GetWindowRect(parentMenu->hWnd, &rc);
3544 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3545 rc.bottom = 0;
3546 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3550 return oldflags;
3554 /*******************************************************************
3555 * GetMenuStringA (USER32.@)
3557 INT WINAPI GetMenuStringA(
3558 HMENU hMenu, /* [in] menuhandle */
3559 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3560 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3561 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3562 UINT wFlags /* [in] MF_ flags */
3564 MENUITEM *item;
3566 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3567 if (str && nMaxSiz) str[0] = '\0';
3568 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3569 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3570 return 0;
3572 if (!item->text) return 0;
3573 if (!str || !nMaxSiz) return strlenW(item->text);
3574 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3575 str[nMaxSiz-1] = 0;
3576 TRACE("returning '%s'\n", str );
3577 return strlen(str);
3581 /*******************************************************************
3582 * GetMenuStringW (USER32.@)
3584 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3585 LPWSTR str, INT nMaxSiz, UINT wFlags )
3587 MENUITEM *item;
3589 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3590 if (str && nMaxSiz) str[0] = '\0';
3591 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3592 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3593 return 0;
3595 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3596 if( !(item->text)) {
3597 str[0] = 0;
3598 return 0;
3600 lstrcpynW( str, item->text, nMaxSiz );
3601 return strlenW(str);
3605 /**********************************************************************
3606 * HiliteMenuItem (USER32.@)
3608 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3609 UINT wHilite )
3611 LPPOPUPMENU menu;
3612 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3613 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3614 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3615 if (menu->FocusedItem == wItemID) return TRUE;
3616 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3617 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3618 return TRUE;
3622 /**********************************************************************
3623 * GetMenuState (USER32.@)
3625 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3627 MENUITEM *item;
3628 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3629 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3630 debug_print_menuitem (" item: ", item, "");
3631 if (item->fType & MF_POPUP)
3633 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3634 if (!menu) return -1;
3635 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3637 else
3639 /* We used to (from way back then) mask the result to 0xff. */
3640 /* I don't know why and it seems wrong as the documented */
3641 /* return flag MF_SEPARATOR is outside that mask. */
3642 return (item->fType | item->fState);
3647 /**********************************************************************
3648 * GetMenuItemCount (USER32.@)
3650 INT WINAPI GetMenuItemCount( HMENU hMenu )
3652 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3653 if (!menu) return -1;
3654 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3655 return menu->nItems;
3659 /**********************************************************************
3660 * GetMenuItemID (USER32.@)
3662 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3664 MENUITEM * lpmi;
3666 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3667 if (lpmi->fType & MF_POPUP) return -1;
3668 return lpmi->wID;
3673 /*******************************************************************
3674 * InsertMenuW (USER32.@)
3676 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3677 UINT_PTR id, LPCWSTR str )
3679 MENUITEM *item;
3681 if (IS_STRING_ITEM(flags) && str)
3682 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3683 hMenu, pos, flags, id, debugstr_w(str) );
3684 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3685 hMenu, pos, flags, id, str );
3687 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3689 if (!(MENU_SetItemData( item, flags, id, str )))
3691 RemoveMenu( hMenu, pos, flags );
3692 return FALSE;
3695 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3696 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3698 item->hCheckBit = item->hUnCheckBit = 0;
3699 return TRUE;
3703 /*******************************************************************
3704 * InsertMenuA (USER32.@)
3706 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3707 UINT_PTR id, LPCSTR str )
3709 BOOL ret = FALSE;
3711 if (IS_STRING_ITEM(flags) && str)
3713 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3714 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3715 if (newstr)
3717 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3718 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3719 HeapFree( GetProcessHeap(), 0, newstr );
3721 return ret;
3723 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3727 /*******************************************************************
3728 * AppendMenuA (USER32.@)
3730 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3731 UINT_PTR id, LPCSTR data )
3733 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3737 /*******************************************************************
3738 * AppendMenuW (USER32.@)
3740 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3741 UINT_PTR id, LPCWSTR data )
3743 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3747 /**********************************************************************
3748 * RemoveMenu (USER32.@)
3750 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3752 LPPOPUPMENU menu;
3753 MENUITEM *item;
3755 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3756 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3757 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3759 /* Remove item */
3761 MENU_FreeItemData( item );
3763 if (--menu->nItems == 0)
3765 HeapFree( GetProcessHeap(), 0, menu->items );
3766 menu->items = NULL;
3768 else
3770 while(nPos < menu->nItems)
3772 *item = *(item+1);
3773 item++;
3774 nPos++;
3776 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3777 menu->nItems * sizeof(MENUITEM) );
3779 return TRUE;
3783 /**********************************************************************
3784 * DeleteMenu (USER32.@)
3786 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3788 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3789 if (!item) return FALSE;
3790 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3791 /* nPos is now the position of the item */
3792 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3793 return TRUE;
3797 /*******************************************************************
3798 * ModifyMenuW (USER32.@)
3800 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3801 UINT_PTR id, LPCWSTR str )
3803 MENUITEM *item;
3805 if (IS_STRING_ITEM(flags))
3806 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3807 else
3808 TRACE("%p %d %04x %04x %p\n", hMenu, pos, flags, id, str );
3810 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3811 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3812 return MENU_SetItemData( item, flags, id, str );
3816 /*******************************************************************
3817 * ModifyMenuA (USER32.@)
3819 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3820 UINT_PTR id, LPCSTR str )
3822 BOOL ret = FALSE;
3824 if (IS_STRING_ITEM(flags) && str)
3826 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3827 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3828 if (newstr)
3830 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3831 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3832 HeapFree( GetProcessHeap(), 0, newstr );
3834 return ret;
3836 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3840 /**********************************************************************
3841 * CreatePopupMenu (USER32.@)
3843 HMENU WINAPI CreatePopupMenu(void)
3845 HMENU hmenu;
3846 POPUPMENU *menu;
3848 if (!(hmenu = CreateMenu())) return 0;
3849 menu = MENU_GetMenu( hmenu );
3850 menu->wFlags |= MF_POPUP;
3851 menu->bTimeToHide = FALSE;
3852 return hmenu;
3856 /**********************************************************************
3857 * GetMenuCheckMarkDimensions (USER.417)
3858 * GetMenuCheckMarkDimensions (USER32.@)
3860 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3862 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3866 /**********************************************************************
3867 * SetMenuItemBitmaps (USER32.@)
3869 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3870 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3872 MENUITEM *item;
3873 TRACE("(%p, %04x, %04x, %p, %p)\n",
3874 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3875 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3877 if (!hNewCheck && !hNewUnCheck)
3879 item->fState &= ~MF_USECHECKBITMAPS;
3881 else /* Install new bitmaps */
3883 item->hCheckBit = hNewCheck;
3884 item->hUnCheckBit = hNewUnCheck;
3885 item->fState |= MF_USECHECKBITMAPS;
3887 return TRUE;
3891 /**********************************************************************
3892 * CreateMenu (USER32.@)
3894 HMENU WINAPI CreateMenu(void)
3896 HMENU hMenu;
3897 LPPOPUPMENU menu;
3898 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3899 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3901 ZeroMemory(menu, sizeof(POPUPMENU));
3902 menu->wMagic = MENU_MAGIC;
3903 menu->FocusedItem = NO_SELECTED_ITEM;
3904 menu->bTimeToHide = FALSE;
3906 TRACE("return %p\n", hMenu );
3908 return hMenu;
3912 /**********************************************************************
3913 * DestroyMenu (USER32.@)
3915 BOOL WINAPI DestroyMenu( HMENU hMenu )
3917 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3919 TRACE("(%p)\n", hMenu);
3922 if (!lppop) return FALSE;
3924 lppop->wMagic = 0; /* Mark it as destroyed */
3926 /* DestroyMenu should not destroy system menu popup owner */
3927 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3929 DestroyWindow( lppop->hWnd );
3930 lppop->hWnd = 0;
3933 if (lppop->items) /* recursively destroy submenus */
3935 int i;
3936 MENUITEM *item = lppop->items;
3937 for (i = lppop->nItems; i > 0; i--, item++)
3939 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3940 MENU_FreeItemData( item );
3942 HeapFree( GetProcessHeap(), 0, lppop->items );
3944 USER_HEAP_FREE( hMenu );
3945 return TRUE;
3949 /**********************************************************************
3950 * GetSystemMenu (USER32.@)
3952 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3954 WND *wndPtr = WIN_GetPtr( hWnd );
3955 HMENU retvalue = 0;
3957 if (wndPtr == WND_DESKTOP) return 0;
3958 if (wndPtr == WND_OTHER_PROCESS)
3960 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3962 else if (wndPtr)
3964 if (wndPtr->hSysMenu && bRevert)
3966 DestroyMenu(wndPtr->hSysMenu);
3967 wndPtr->hSysMenu = 0;
3970 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3971 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
3973 if( wndPtr->hSysMenu )
3975 POPUPMENU *menu;
3976 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3978 /* Store the dummy sysmenu handle to facilitate the refresh */
3979 /* of the close button if the SC_CLOSE item change */
3980 menu = MENU_GetMenu(retvalue);
3981 if ( menu )
3982 menu->hSysMenuOwner = wndPtr->hSysMenu;
3984 WIN_ReleasePtr( wndPtr );
3986 return bRevert ? 0 : retvalue;
3990 /*******************************************************************
3991 * SetSystemMenu (USER32.@)
3993 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3995 WND *wndPtr = WIN_GetPtr( hwnd );
3997 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3999 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4000 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4001 WIN_ReleasePtr( wndPtr );
4002 return TRUE;
4004 return FALSE;
4008 /**********************************************************************
4009 * GetMenu (USER32.@)
4011 HMENU WINAPI GetMenu( HWND hWnd )
4013 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4014 TRACE("for %p returning %p\n", hWnd, retvalue);
4015 return retvalue;
4018 /**********************************************************************
4019 * GetMenuBarInfo (USER32.@)
4021 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4023 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
4024 return FALSE;
4027 /**********************************************************************
4028 * MENU_SetMenu
4030 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4031 * SetWindowPos call that would result if SetMenu were called directly.
4033 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4035 TRACE("(%p, %p);\n", hWnd, hMenu);
4037 if (hMenu && !IsMenu(hMenu))
4039 WARN("hMenu %p is not a menu handle\n", hMenu);
4040 SetLastError(ERROR_INVALID_MENU_HANDLE);
4041 return FALSE;
4043 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4044 return FALSE;
4046 hWnd = WIN_GetFullHandle( hWnd );
4047 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4049 if (hMenu != 0)
4051 LPPOPUPMENU lpmenu;
4053 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4055 lpmenu->hWnd = hWnd;
4056 lpmenu->Height = 0; /* Make sure we recalculate the size */
4058 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4059 return TRUE;
4063 /**********************************************************************
4064 * SetMenu (USER32.@)
4066 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4068 if(!MENU_SetMenu(hWnd, hMenu))
4069 return FALSE;
4071 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4072 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4073 return TRUE;
4077 /**********************************************************************
4078 * GetSubMenu (USER32.@)
4080 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4082 MENUITEM * lpmi;
4084 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4085 if (!(lpmi->fType & MF_POPUP)) return 0;
4086 return lpmi->hSubMenu;
4090 /**********************************************************************
4091 * DrawMenuBar (USER32.@)
4093 BOOL WINAPI DrawMenuBar( HWND hWnd )
4095 LPPOPUPMENU lppop;
4096 HMENU hMenu = GetMenu(hWnd);
4098 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4099 return FALSE;
4100 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4102 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4103 lppop->hwndOwner = hWnd;
4104 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4105 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4106 return TRUE;
4109 /***********************************************************************
4110 * DrawMenuBarTemp (USER32.@)
4112 * UNDOCUMENTED !!
4114 * called by W98SE desk.cpl Control Panel Applet
4116 * Not 100% sure about the param names, but close.
4118 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4120 LPPOPUPMENU lppop;
4121 UINT i,retvalue;
4122 HFONT hfontOld = 0;
4123 BOOL flat_menu = FALSE;
4125 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4127 if (!hMenu)
4128 hMenu = GetMenu(hwnd);
4130 if (!hFont)
4131 hFont = get_menu_font(FALSE);
4133 lppop = MENU_GetMenu( hMenu );
4134 if (lppop == NULL || lprect == NULL)
4136 retvalue = GetSystemMetrics(SM_CYMENU);
4137 goto END;
4140 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4142 hfontOld = SelectObject( hDC, hFont);
4144 if (lppop->Height == 0)
4145 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4147 lprect->bottom = lprect->top + lppop->Height;
4149 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4151 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4152 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4153 LineTo( hDC, lprect->right, lprect->bottom );
4155 if (lppop->nItems == 0)
4157 retvalue = GetSystemMetrics(SM_CYMENU);
4158 goto END;
4161 for (i = 0; i < lppop->nItems; i++)
4163 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4164 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4166 retvalue = lppop->Height;
4168 END:
4169 if (hfontOld) SelectObject (hDC, hfontOld);
4170 return retvalue;
4173 /***********************************************************************
4174 * EndMenu (USER.187)
4175 * EndMenu (USER32.@)
4177 void WINAPI EndMenu(void)
4179 /* if we are in the menu code, and it is active */
4180 if (!fEndMenu && top_popup)
4182 /* terminate the menu handling code */
4183 fEndMenu = TRUE;
4185 /* needs to be posted to wakeup the internal menu handler */
4186 /* which will now terminate the menu, in the event that */
4187 /* the main window was minimized, or lost focus, so we */
4188 /* don't end up with an orphaned menu */
4189 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4194 /***********************************************************************
4195 * LookupMenuHandle (USER.217)
4197 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4199 HMENU hmenu32 = HMENU_32(hmenu);
4200 UINT id32 = id;
4201 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4202 else return HMENU_16(hmenu32);
4206 /**********************************************************************
4207 * LoadMenu (USER.150)
4209 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4211 HRSRC16 hRsrc;
4212 HGLOBAL16 handle;
4213 HMENU16 hMenu;
4215 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4216 if (!name) return 0;
4218 instance = GetExePtr( instance );
4219 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4220 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4221 hMenu = LoadMenuIndirect16(LockResource16(handle));
4222 FreeResource16( handle );
4223 return hMenu;
4227 /*****************************************************************
4228 * LoadMenuA (USER32.@)
4230 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4232 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4233 if (!hrsrc) return 0;
4234 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4238 /*****************************************************************
4239 * LoadMenuW (USER32.@)
4241 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4243 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4244 if (!hrsrc) return 0;
4245 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4249 /**********************************************************************
4250 * LoadMenuIndirect (USER.220)
4252 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4254 HMENU hMenu;
4255 WORD version, offset;
4256 LPCSTR p = (LPCSTR)template;
4258 TRACE("(%p)\n", template );
4259 version = GET_WORD(p);
4260 p += sizeof(WORD);
4261 if (version)
4263 WARN("version must be 0 for Win16\n" );
4264 return 0;
4266 offset = GET_WORD(p);
4267 p += sizeof(WORD) + offset;
4268 if (!(hMenu = CreateMenu())) return 0;
4269 if (!MENU_ParseResource( p, hMenu, FALSE ))
4271 DestroyMenu( hMenu );
4272 return 0;
4274 return HMENU_16(hMenu);
4278 /**********************************************************************
4279 * LoadMenuIndirectW (USER32.@)
4281 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4283 HMENU hMenu;
4284 WORD version, offset;
4285 LPCSTR p = (LPCSTR)template;
4287 version = GET_WORD(p);
4288 p += sizeof(WORD);
4289 TRACE("%p, ver %d\n", template, version );
4290 switch (version)
4292 case 0: /* standard format is version of 0 */
4293 offset = GET_WORD(p);
4294 p += sizeof(WORD) + offset;
4295 if (!(hMenu = CreateMenu())) return 0;
4296 if (!MENU_ParseResource( p, hMenu, TRUE ))
4298 DestroyMenu( hMenu );
4299 return 0;
4301 return hMenu;
4302 case 1: /* extended format is version of 1 */
4303 offset = GET_WORD(p);
4304 p += sizeof(WORD) + offset;
4305 if (!(hMenu = CreateMenu())) return 0;
4306 if (!MENUEX_ParseResource( p, hMenu))
4308 DestroyMenu( hMenu );
4309 return 0;
4311 return hMenu;
4312 default:
4313 ERR("version %d not supported.\n", version);
4314 return 0;
4319 /**********************************************************************
4320 * LoadMenuIndirectA (USER32.@)
4322 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4324 return LoadMenuIndirectW( template );
4328 /**********************************************************************
4329 * IsMenu (USER32.@)
4331 BOOL WINAPI IsMenu(HMENU hmenu)
4333 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4334 return menu != NULL;
4337 /**********************************************************************
4338 * GetMenuItemInfo_common
4341 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4342 LPMENUITEMINFOW lpmii, BOOL unicode)
4344 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4346 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4348 if (!menu)
4349 return FALSE;
4351 if( lpmii->fMask & MIIM_TYPE) {
4352 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4353 WARN("invalid combination of fMask bits used\n");
4354 /* this does not happen on Win9x/ME */
4355 SetLastError( ERROR_INVALID_PARAMETER);
4356 return FALSE;
4358 lpmii->fType = menu->fType & ~MF_POPUP;
4359 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4360 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4361 if( lpmii->fType & MFT_BITMAP) {
4362 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4363 lpmii->cch = 0;
4364 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4365 /* this does not happen on Win9x/ME */
4366 lpmii->dwTypeData = 0;
4367 lpmii->cch = 0;
4371 /* copy the text string */
4372 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4373 if( !menu->text ) {
4374 if(lpmii->dwTypeData && lpmii->cch) {
4375 lpmii->cch = 0;
4376 if( unicode)
4377 *((WCHAR *)lpmii->dwTypeData) = 0;
4378 else
4379 *((CHAR *)lpmii->dwTypeData) = 0;
4381 } else {
4382 int len;
4383 if (unicode)
4385 len = strlenW(menu->text);
4386 if(lpmii->dwTypeData && lpmii->cch)
4387 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4389 else
4391 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4392 0, NULL, NULL ) - 1;
4393 if(lpmii->dwTypeData && lpmii->cch)
4394 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4395 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4396 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4398 /* if we've copied a substring we return its length */
4399 if(lpmii->dwTypeData && lpmii->cch)
4400 if (lpmii->cch <= len + 1)
4401 lpmii->cch--;
4402 else
4403 lpmii->cch = len;
4404 else {
4405 /* return length of string */
4406 /* not on Win9x/ME if fType & MFT_BITMAP */
4407 lpmii->cch = len;
4412 if (lpmii->fMask & MIIM_FTYPE)
4413 lpmii->fType = menu->fType & ~MF_POPUP;
4415 if (lpmii->fMask & MIIM_BITMAP)
4416 lpmii->hbmpItem = menu->hbmpItem;
4418 if (lpmii->fMask & MIIM_STATE)
4419 lpmii->fState = menu->fState;
4421 if (lpmii->fMask & MIIM_ID)
4422 lpmii->wID = menu->wID;
4424 if (lpmii->fMask & MIIM_SUBMENU)
4425 lpmii->hSubMenu = menu->hSubMenu;
4426 else {
4427 /* hSubMenu is always cleared
4428 * (not on Win9x/ME ) */
4429 lpmii->hSubMenu = 0;
4432 if (lpmii->fMask & MIIM_CHECKMARKS) {
4433 lpmii->hbmpChecked = menu->hCheckBit;
4434 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4436 if (lpmii->fMask & MIIM_DATA)
4437 lpmii->dwItemData = menu->dwItemData;
4439 return TRUE;
4442 /**********************************************************************
4443 * GetMenuItemInfoA (USER32.@)
4445 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4446 LPMENUITEMINFOA lpmii)
4448 BOOL ret;
4449 MENUITEMINFOA mii;
4450 if( lpmii->cbSize != sizeof( mii) &&
4451 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4452 SetLastError( ERROR_INVALID_PARAMETER);
4453 return FALSE;
4455 memcpy( &mii, lpmii, lpmii->cbSize);
4456 mii.cbSize = sizeof( mii);
4457 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4458 (LPMENUITEMINFOW)&mii, FALSE);
4459 mii.cbSize = lpmii->cbSize;
4460 memcpy( lpmii, &mii, mii.cbSize);
4461 return ret;
4464 /**********************************************************************
4465 * GetMenuItemInfoW (USER32.@)
4467 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4468 LPMENUITEMINFOW lpmii)
4470 BOOL ret;
4471 MENUITEMINFOW mii;
4472 if( lpmii->cbSize != sizeof( mii) &&
4473 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4474 SetLastError( ERROR_INVALID_PARAMETER);
4475 return FALSE;
4477 memcpy( &mii, lpmii, lpmii->cbSize);
4478 mii.cbSize = sizeof( mii);
4479 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4480 mii.cbSize = lpmii->cbSize;
4481 memcpy( lpmii, &mii, mii.cbSize);
4482 return ret;
4486 /* set a menu item text from a ASCII or Unicode string */
4487 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4489 if (!text)
4490 menu->text = NULL;
4491 else if (unicode)
4493 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4494 strcpyW( menu->text, text );
4496 else
4498 LPCSTR str = (LPCSTR)text;
4499 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4500 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4501 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4506 /**********************************************************************
4507 * SetMenuItemInfo_common
4510 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4511 const MENUITEMINFOW *lpmii,
4512 BOOL unicode)
4514 if (!menu) return FALSE;
4516 debug_print_menuitem("SetmenuItemInfo_common from: ", menu, "");
4518 if (lpmii->fMask & MIIM_TYPE ) {
4519 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4520 WARN("invalid combination of fMask bits used\n");
4521 /* this does not happen on Win9x/ME */
4522 SetLastError( ERROR_INVALID_PARAMETER);
4523 return FALSE;
4525 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4526 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4527 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4529 if (IS_STRING_ITEM(menu->fType)) {
4530 HeapFree(GetProcessHeap(), 0, menu->text);
4531 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4532 } else if( (menu->fType) & MFT_BITMAP)
4533 menu->hbmpItem = (HBITMAP)lpmii->dwTypeData;
4536 if (lpmii->fMask & MIIM_FTYPE ) {
4537 if(( lpmii->fType & MFT_BITMAP)) {
4538 SetLastError( ERROR_INVALID_PARAMETER);
4539 return FALSE;
4541 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4542 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4544 if (lpmii->fMask & MIIM_STRING ) {
4545 /* free the string when used */
4546 HeapFree(GetProcessHeap(), 0, menu->text);
4547 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4550 if (lpmii->fMask & MIIM_STATE)
4552 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4553 menu->fState = lpmii->fState;
4556 if (lpmii->fMask & MIIM_ID)
4557 menu->wID = lpmii->wID;
4559 if (lpmii->fMask & MIIM_SUBMENU) {
4560 menu->hSubMenu = lpmii->hSubMenu;
4561 if (menu->hSubMenu) {
4562 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4563 if (subMenu) {
4564 subMenu->wFlags |= MF_POPUP;
4565 menu->fType |= MF_POPUP;
4567 else {
4568 SetLastError( ERROR_INVALID_PARAMETER);
4569 return FALSE;
4572 else
4573 menu->fType &= ~MF_POPUP;
4576 if (lpmii->fMask & MIIM_CHECKMARKS)
4578 if (lpmii->fType & MFT_RADIOCHECK)
4579 menu->fType |= MFT_RADIOCHECK;
4581 menu->hCheckBit = lpmii->hbmpChecked;
4582 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4584 if (lpmii->fMask & MIIM_DATA)
4585 menu->dwItemData = lpmii->dwItemData;
4587 if (lpmii->fMask & MIIM_BITMAP)
4588 menu->hbmpItem = lpmii->hbmpItem;
4590 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4591 menu->fType |= MFT_SEPARATOR;
4593 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4594 return TRUE;
4597 /**********************************************************************
4598 * SetMenuItemInfoA (USER32.@)
4600 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4601 const MENUITEMINFOA *lpmii)
4603 MENUITEMINFOA mii;
4604 if( lpmii->cbSize != sizeof( mii) &&
4605 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4606 SetLastError( ERROR_INVALID_PARAMETER);
4607 return FALSE;
4609 memcpy( &mii, lpmii, lpmii->cbSize);
4610 if( lpmii->cbSize != sizeof( mii)) {
4611 mii.cbSize = sizeof( mii);
4612 mii.hbmpItem = NULL;
4614 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4615 (const MENUITEMINFOW *)&mii, FALSE);
4618 /**********************************************************************
4619 * SetMenuItemInfoW (USER32.@)
4621 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4622 const MENUITEMINFOW *lpmii)
4624 MENUITEMINFOW mii;
4625 if( lpmii->cbSize != sizeof( mii) &&
4626 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4627 SetLastError( ERROR_INVALID_PARAMETER);
4628 return FALSE;
4630 memcpy( &mii, lpmii, lpmii->cbSize);
4631 if( lpmii->cbSize != sizeof( mii)) {
4632 mii.cbSize = sizeof( mii);
4633 mii.hbmpItem = NULL;
4635 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4636 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4639 /**********************************************************************
4640 * SetMenuDefaultItem (USER32.@)
4643 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4645 UINT i;
4646 POPUPMENU *menu;
4647 MENUITEM *item;
4649 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4651 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4653 /* reset all default-item flags */
4654 item = menu->items;
4655 for (i = 0; i < menu->nItems; i++, item++)
4657 item->fState &= ~MFS_DEFAULT;
4660 /* no default item */
4661 if ( -1 == uItem)
4663 return TRUE;
4666 item = menu->items;
4667 if ( bypos )
4669 if ( uItem >= menu->nItems ) return FALSE;
4670 item[uItem].fState |= MFS_DEFAULT;
4671 return TRUE;
4673 else
4675 for (i = 0; i < menu->nItems; i++, item++)
4677 if (item->wID == uItem)
4679 item->fState |= MFS_DEFAULT;
4680 return TRUE;
4685 return FALSE;
4688 /**********************************************************************
4689 * GetMenuDefaultItem (USER32.@)
4691 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4693 POPUPMENU *menu;
4694 MENUITEM * item;
4695 UINT i = 0;
4697 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4699 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4701 /* find default item */
4702 item = menu->items;
4704 /* empty menu */
4705 if (! item) return -1;
4707 while ( !( item->fState & MFS_DEFAULT ) )
4709 i++; item++;
4710 if (i >= menu->nItems ) return -1;
4713 /* default: don't return disabled items */
4714 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4716 /* search rekursiv when needed */
4717 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4719 UINT ret;
4720 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4721 if ( -1 != ret ) return ret;
4723 /* when item not found in submenu, return the popup item */
4725 return ( bypos ) ? i : item->wID;
4730 /**********************************************************************
4731 * InsertMenuItemA (USER32.@)
4733 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4734 const MENUITEMINFOA *lpmii)
4736 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4737 MENUITEMINFOA mii;
4738 if( lpmii->cbSize != sizeof( mii) &&
4739 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4740 SetLastError( ERROR_INVALID_PARAMETER);
4741 return FALSE;
4743 memcpy( &mii, lpmii, lpmii->cbSize);
4744 if( lpmii->cbSize != sizeof( mii)) {
4745 mii.cbSize = sizeof( mii);
4746 mii.hbmpItem = NULL;
4748 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4752 /**********************************************************************
4753 * InsertMenuItemW (USER32.@)
4755 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4756 const MENUITEMINFOW *lpmii)
4758 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4759 MENUITEMINFOW mii;
4760 if( lpmii->cbSize != sizeof( mii) &&
4761 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4762 SetLastError( ERROR_INVALID_PARAMETER);
4763 return FALSE;
4765 memcpy( &mii, lpmii, lpmii->cbSize);
4766 if( lpmii->cbSize != sizeof( mii)) {
4767 mii.cbSize = sizeof( mii);
4768 mii.hbmpItem = NULL;
4770 return SetMenuItemInfo_common(item, &mii, TRUE);
4773 /**********************************************************************
4774 * CheckMenuRadioItem (USER32.@)
4777 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4778 UINT first, UINT last, UINT check,
4779 UINT bypos)
4781 MENUITEM *mifirst, *milast, *micheck;
4782 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4784 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4786 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4787 milast = MENU_FindItem (&mlast, &last, bypos);
4788 micheck = MENU_FindItem (&mcheck, &check, bypos);
4790 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4791 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4792 micheck > milast || micheck < mifirst)
4793 return FALSE;
4795 while (mifirst <= milast)
4797 if (mifirst == micheck)
4799 mifirst->fType |= MFT_RADIOCHECK;
4800 mifirst->fState |= MFS_CHECKED;
4801 } else {
4802 mifirst->fType &= ~MFT_RADIOCHECK;
4803 mifirst->fState &= ~MFS_CHECKED;
4805 mifirst++;
4808 return TRUE;
4812 /**********************************************************************
4813 * GetMenuItemRect (USER32.@)
4815 * ATTENTION: Here, the returned values in rect are the screen
4816 * coordinates of the item just like if the menu was
4817 * always on the upper left side of the application.
4820 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4821 LPRECT rect)
4823 POPUPMENU *itemMenu;
4824 MENUITEM *item;
4825 HWND referenceHwnd;
4827 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4829 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4830 referenceHwnd = hwnd;
4832 if(!hwnd)
4834 itemMenu = MENU_GetMenu(hMenu);
4835 if (itemMenu == NULL)
4836 return FALSE;
4838 if(itemMenu->hWnd == 0)
4839 return FALSE;
4840 referenceHwnd = itemMenu->hWnd;
4843 if ((rect == NULL) || (item == NULL))
4844 return FALSE;
4846 *rect = item->rect;
4848 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4850 return TRUE;
4854 /**********************************************************************
4855 * SetMenuInfo (USER32.@)
4857 * FIXME
4858 * MIM_APPLYTOSUBMENUS
4859 * actually use the items to draw the menu
4861 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4863 POPUPMENU *menu;
4865 TRACE("(%p %p)\n", hMenu, lpmi);
4867 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4870 if (lpmi->fMask & MIM_BACKGROUND)
4871 menu->hbrBack = lpmi->hbrBack;
4873 if (lpmi->fMask & MIM_HELPID)
4874 menu->dwContextHelpID = lpmi->dwContextHelpID;
4876 if (lpmi->fMask & MIM_MAXHEIGHT)
4877 menu->cyMax = lpmi->cyMax;
4879 if (lpmi->fMask & MIM_MENUDATA)
4880 menu->dwMenuData = lpmi->dwMenuData;
4882 if (lpmi->fMask & MIM_STYLE)
4884 menu->dwStyle = lpmi->dwStyle;
4885 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4886 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4887 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4888 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4891 return TRUE;
4893 return FALSE;
4896 /**********************************************************************
4897 * GetMenuInfo (USER32.@)
4899 * NOTES
4900 * win98/NT5.0
4903 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4904 { POPUPMENU *menu;
4906 TRACE("(%p %p)\n", hMenu, lpmi);
4908 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4911 if (lpmi->fMask & MIM_BACKGROUND)
4912 lpmi->hbrBack = menu->hbrBack;
4914 if (lpmi->fMask & MIM_HELPID)
4915 lpmi->dwContextHelpID = menu->dwContextHelpID;
4917 if (lpmi->fMask & MIM_MAXHEIGHT)
4918 lpmi->cyMax = menu->cyMax;
4920 if (lpmi->fMask & MIM_MENUDATA)
4921 lpmi->dwMenuData = menu->dwMenuData;
4923 if (lpmi->fMask & MIM_STYLE)
4924 lpmi->dwStyle = menu->dwStyle;
4926 return TRUE;
4928 return FALSE;
4932 /**********************************************************************
4933 * SetMenuContextHelpId (USER32.@)
4935 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4937 LPPOPUPMENU menu;
4939 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4941 if ((menu = MENU_GetMenu(hMenu)))
4943 menu->dwContextHelpID = dwContextHelpID;
4944 return TRUE;
4946 return FALSE;
4950 /**********************************************************************
4951 * GetMenuContextHelpId (USER32.@)
4953 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4955 LPPOPUPMENU menu;
4957 TRACE("(%p)\n", hMenu);
4959 if ((menu = MENU_GetMenu(hMenu)))
4961 return menu->dwContextHelpID;
4963 return 0;
4966 /**********************************************************************
4967 * MenuItemFromPoint (USER32.@)
4969 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4971 POPUPMENU *menu = MENU_GetMenu(hMenu);
4972 UINT pos;
4974 /*FIXME: Do we have to handle hWnd here? */
4975 if (!menu) return -1;
4976 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4977 return pos;
4981 /**********************************************************************
4982 * translate_accelerator
4984 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4985 BYTE fVirt, WORD key, WORD cmd )
4987 INT mask = 0;
4988 UINT mesg = 0;
4990 if (wParam != key) return FALSE;
4992 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4993 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4994 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4996 if (message == WM_CHAR || message == WM_SYSCHAR)
4998 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5000 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
5001 goto found;
5004 else
5006 if(fVirt & FVIRTKEY)
5008 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5009 wParam, 0xff & HIWORD(lParam));
5011 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5012 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5014 else
5016 if (!(lParam & 0x01000000)) /* no special_key */
5018 if ((fVirt & FALT) && (lParam & 0x20000000))
5019 { /* ^^ ALT pressed */
5020 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5021 goto found;
5026 return FALSE;
5028 found:
5029 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5030 mesg = 1;
5031 else
5033 HMENU hMenu, hSubMenu, hSysMenu;
5034 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5036 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5037 hSysMenu = get_win_sys_menu( hWnd );
5039 /* find menu item and ask application to initialize it */
5040 /* 1. in the system menu */
5041 hSubMenu = hSysMenu;
5042 nPos = cmd;
5043 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5045 if (GetCapture())
5046 mesg = 2;
5047 if (!IsWindowEnabled(hWnd))
5048 mesg = 3;
5049 else
5051 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5052 if(hSubMenu != hSysMenu)
5054 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5055 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5056 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5058 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5061 else /* 2. in the window's menu */
5063 hSubMenu = hMenu;
5064 nPos = cmd;
5065 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5067 if (GetCapture())
5068 mesg = 2;
5069 if (!IsWindowEnabled(hWnd))
5070 mesg = 3;
5071 else
5073 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5074 if(hSubMenu != hMenu)
5076 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5077 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5078 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5080 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5085 if (mesg == 0)
5087 if (uSysStat != (UINT)-1)
5089 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5090 mesg=4;
5091 else
5092 mesg=WM_SYSCOMMAND;
5094 else
5096 if (uStat != (UINT)-1)
5098 if (IsIconic(hWnd))
5099 mesg=5;
5100 else
5102 if (uStat & (MF_DISABLED|MF_GRAYED))
5103 mesg=6;
5104 else
5105 mesg=WM_COMMAND;
5108 else
5109 mesg=WM_COMMAND;
5114 if( mesg==WM_COMMAND )
5116 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5117 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5119 else if( mesg==WM_SYSCOMMAND )
5121 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5122 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5124 else
5126 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5127 * #0: unknown (please report!)
5128 * #1: for WM_KEYUP,WM_SYSKEYUP
5129 * #2: mouse is captured
5130 * #3: window is disabled
5131 * #4: it's a disabled system menu option
5132 * #5: it's a menu option, but window is iconic
5133 * #6: it's a menu option, but disabled
5135 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5136 if(mesg==0)
5137 ERR_(accel)(" unknown reason - please report!\n");
5139 return TRUE;
5142 /**********************************************************************
5143 * TranslateAcceleratorA (USER32.@)
5144 * TranslateAccelerator (USER32.@)
5146 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5148 /* YES, Accel16! */
5149 LPACCEL16 lpAccelTbl;
5150 int i;
5151 WPARAM wParam;
5153 if (!hWnd || !msg) return 0;
5155 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5157 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5158 return 0;
5161 wParam = msg->wParam;
5163 switch (msg->message)
5165 case WM_KEYDOWN:
5166 case WM_SYSKEYDOWN:
5167 break;
5169 case WM_CHAR:
5170 case WM_SYSCHAR:
5172 char ch = LOWORD(wParam);
5173 WCHAR wch;
5174 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5175 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5177 break;
5179 default:
5180 return 0;
5183 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5184 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5185 i = 0;
5188 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5189 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5190 return 1;
5191 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5193 return 0;
5196 /**********************************************************************
5197 * TranslateAcceleratorW (USER32.@)
5199 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5201 /* YES, Accel16! */
5202 LPACCEL16 lpAccelTbl;
5203 int i;
5205 if (!hWnd || !msg) return 0;
5207 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5209 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5210 return 0;
5213 switch (msg->message)
5215 case WM_KEYDOWN:
5216 case WM_SYSKEYDOWN:
5217 case WM_CHAR:
5218 case WM_SYSCHAR:
5219 break;
5221 default:
5222 return 0;
5225 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5226 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5227 i = 0;
5230 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5231 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5232 return 1;
5233 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5235 return 0;