menu: Take the MF_DEFAULT flag in to account in MENU_CalcItemSize.
[wine/gsoc_dplay.git] / dlls / user / menu.c
bloba72e9222a32f5be06136244a30dfa161b722c6f2
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
40 * - MNS_NOTIFYBYPOS
43 #include "config.h"
44 #include "wine/port.h"
46 #include <stdarg.h>
47 #include <string.h>
49 #include "windef.h"
50 #include "winbase.h"
51 #include "wingdi.h"
52 #include "winnls.h"
53 #include "wine/winbase16.h"
54 #include "wine/winuser16.h"
55 #include "wownt32.h"
56 #include "wine/server.h"
57 #include "wine/unicode.h"
58 #include "win.h"
59 #include "controls.h"
60 #include "user_private.h"
61 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(menu);
64 WINE_DECLARE_DEBUG_CHANNEL(accel);
66 /* internal popup menu window messages */
68 #define MM_SETMENUHANDLE (WM_USER + 0)
69 #define MM_GETMENUHANDLE (WM_USER + 1)
71 /* Menu item structure */
72 typedef struct {
73 /* ----------- MENUITEMINFO Stuff ----------- */
74 UINT fType; /* Item type. */
75 UINT fState; /* Item state. */
76 UINT_PTR wID; /* Item id. */
77 HMENU hSubMenu; /* Pop-up menu. */
78 HBITMAP hCheckBit; /* Bitmap when checked. */
79 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
80 LPWSTR text; /* Item text. */
81 ULONG_PTR dwItemData; /* Application defined. */
82 LPWSTR dwTypeData; /* depends on fMask */
83 HBITMAP hbmpItem; /* bitmap */
84 /* ----------- Wine stuff ----------- */
85 RECT rect; /* Item area (relative to menu window) */
86 UINT xTab; /* X position of text after Tab */
87 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
88 * bitmap */
89 } MENUITEM;
91 /* Popup menu structure */
92 typedef struct {
93 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD wMagic; /* Magic number */
95 WORD Width; /* Width of the whole menu */
96 WORD Height; /* Height of the whole menu */
97 UINT nItems; /* Number of items in the menu */
98 HWND hWnd; /* Window containing the menu */
99 MENUITEM *items; /* Array of menu items */
100 UINT FocusedItem; /* Currently focused item */
101 HWND hwndOwner; /* window receiving the messages for ownerdraw */
102 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
103 BOOL bScrolling; /* Scroll arrows are active */
104 UINT nScrollPos; /* Current scroll position */
105 UINT nTotalHeight; /* Total height of menu items inside menu */
106 /* ------------ MENUINFO members ------ */
107 DWORD dwStyle; /* Extended menu style */
108 UINT cyMax; /* max height of the whole menu, 0 is screen height */
109 HBRUSH hbrBack; /* brush for menu background */
110 DWORD dwContextHelpID;
111 DWORD dwMenuData; /* application defined value */
112 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
113 SIZE maxBmpSize; /* Maximum size of the bitmap items */
114 } POPUPMENU, *LPPOPUPMENU;
116 /* internal flags for menu tracking */
118 #define TF_ENDMENU 0x0001
119 #define TF_SUSPENDPOPUP 0x0002
120 #define TF_SKIPREMOVE 0x0004
122 typedef struct
124 UINT trackFlags;
125 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
126 HMENU hTopMenu; /* initial menu */
127 HWND hOwnerWnd; /* where notifications are sent */
128 POINT pt;
129 } MTRACKER;
131 #define MENU_MAGIC 0x554d /* 'MU' */
133 #define ITEM_PREV -1
134 #define ITEM_NEXT 1
136 /* Internal MENU_TrackMenu() flags */
137 #define TPM_INTERNAL 0xF0000000
138 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
139 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
140 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
142 /* Space between 2 columns */
143 #define MENU_COL_SPACE 4
145 /* top and bottom margins for popup menus */
146 #define MENU_TOP_MARGIN 3
147 #define MENU_BOTTOM_MARGIN 2
149 /* (other menu->FocusedItem values give the position of the focused item) */
150 #define NO_SELECTED_ITEM 0xffff
152 #define MENU_ITEM_TYPE(flags) \
153 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
155 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
156 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
157 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
159 #define IS_SYSTEM_MENU(menu) \
160 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
162 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
163 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
164 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
165 MF_POPUP | MF_SYSMENU | MF_HELP)
166 #define STATE_MASK (~TYPE_MASK)
168 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
170 static SIZE menucharsize;
171 static UINT ODitemheight; /* default owner drawn item height */
173 /* Use global popup window because there's no way 2 menus can
174 * be tracked at the same time. */
175 static HWND top_popup;
177 /* Flag set by EndMenu() to force an exit from menu tracking */
178 static BOOL fEndMenu = FALSE;
180 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
182 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
184 /*********************************************************************
185 * menu class descriptor
187 const struct builtin_class_descr MENU_builtin_class =
189 POPUPMENU_CLASS_ATOMA, /* name */
190 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
191 NULL, /* procA (winproc is Unicode only) */
192 PopupMenuWndProc, /* procW */
193 sizeof(HMENU), /* extra */
194 IDC_ARROW, /* cursor */
195 (HBRUSH)(COLOR_MENU+1) /* brush */
199 /***********************************************************************
200 * debug_print_menuitem
202 * Print a menuitem in readable form.
205 #define debug_print_menuitem(pre, mp, post) \
206 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
208 #define MENUOUT(text) \
209 TRACE("%s%s", (count++ ? "," : ""), (text))
211 #define MENUFLAG(bit,text) \
212 do { \
213 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
214 } while (0)
216 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
217 const char *postfix)
219 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
220 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "HBMMENU_MBAR_CLOSE",
221 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
222 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
223 TRACE("%s ", prefix);
224 if (mp) {
225 UINT flags = mp->fType;
226 TRACE( "{ ID=0x%x", mp->wID);
227 if ( mp->hSubMenu)
228 TRACE( ", Sub=%p", mp->hSubMenu);
229 if (flags) {
230 int count = 0;
231 TRACE( ", fType=");
232 MENUFLAG( MFT_SEPARATOR, "sep");
233 MENUFLAG( MFT_OWNERDRAW, "own");
234 MENUFLAG( MFT_BITMAP, "bit");
235 MENUFLAG(MF_POPUP, "pop");
236 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
237 MENUFLAG(MFT_MENUBREAK, "brk");
238 MENUFLAG(MFT_RADIOCHECK, "radio");
239 MENUFLAG(MFT_RIGHTORDER, "rorder");
240 MENUFLAG(MF_SYSMENU, "sys");
241 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
242 if (flags)
243 TRACE( "+0x%x", flags);
245 flags = mp->fState;
246 if (flags) {
247 int count = 0;
248 TRACE( ", State=");
249 MENUFLAG(MFS_GRAYED, "grey");
250 MENUFLAG(MFS_DEFAULT, "default");
251 MENUFLAG(MFS_DISABLED, "dis");
252 MENUFLAG(MFS_CHECKED, "check");
253 MENUFLAG(MFS_HILITE, "hi");
254 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
255 MENUFLAG(MF_MOUSESELECT, "mouse");
256 if (flags)
257 TRACE( "+0x%x", flags);
259 if (mp->hCheckBit)
260 TRACE( ", Chk=%p", mp->hCheckBit);
261 if (mp->hUnCheckBit)
262 TRACE( ", Unc=%p", mp->hUnCheckBit);
263 if (mp->text)
264 TRACE( ", Text=%s", debugstr_w(mp->text));
265 if (mp->dwItemData)
266 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
267 if (mp->hbmpItem)
269 if( IS_MAGIC_BITMAP(mp->hbmpItem))
270 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
271 else
272 TRACE( ", hbitmap=%p", mp->hbmpItem);
274 TRACE( " }");
275 } else
276 TRACE( "NULL");
277 TRACE(" %s\n", postfix);
280 #undef MENUOUT
281 #undef MENUFLAG
284 /***********************************************************************
285 * MENU_GetMenu
287 * Validate the given menu handle and returns the menu structure pointer.
289 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
291 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
292 if (!menu || menu->wMagic != MENU_MAGIC)
294 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
295 menu = NULL;
297 return menu;
300 /***********************************************************************
301 * get_win_sys_menu
303 * Get the system menu of a window
305 static HMENU get_win_sys_menu( HWND hwnd )
307 HMENU ret = 0;
308 WND *win = WIN_GetPtr( hwnd );
309 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
311 ret = win->hSysMenu;
312 WIN_ReleasePtr( win );
314 return ret;
317 /***********************************************************************
318 * get_menu_font
320 static HFONT get_menu_font( BOOL bold )
322 static HFONT hMenuFont, hMenuFontBold;
324 HFONT ret = bold ? hMenuFontBold : hMenuFont;
326 if (!ret)
328 NONCLIENTMETRICSW ncm;
329 HFONT prev;
331 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
332 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
334 if (bold)
336 ncm.lfMenuFont.lfWeight += 300;
337 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
339 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
340 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
341 ret, NULL );
342 if (prev)
344 /* another thread beat us to it */
345 DeleteObject( ret );
346 ret = prev;
349 return ret;
352 /***********************************************************************
353 * get_arrow_bitmap
355 static HBITMAP get_arrow_bitmap(void)
357 static HBITMAP arrow_bitmap;
359 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
360 return arrow_bitmap;
363 /***********************************************************************
364 * get_down_arrow_bitmap
366 static HBITMAP get_down_arrow_bitmap(void)
368 static HBITMAP arrow_bitmap;
370 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
371 return arrow_bitmap;
374 /***********************************************************************
375 * get_down_arrow_inactive_bitmap
377 static HBITMAP get_down_arrow_inactive_bitmap(void)
379 static HBITMAP arrow_bitmap;
381 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
382 return arrow_bitmap;
385 /***********************************************************************
386 * get_up_arrow_bitmap
388 static HBITMAP get_up_arrow_bitmap(void)
390 static HBITMAP arrow_bitmap;
392 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
393 return arrow_bitmap;
396 /***********************************************************************
397 * get_up_arrow_inactive_bitmap
399 static HBITMAP get_up_arrow_inactive_bitmap(void)
401 static HBITMAP arrow_bitmap;
403 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
404 return arrow_bitmap;
407 /***********************************************************************
408 * MENU_CopySysPopup
410 * Return the default system menu.
412 static HMENU MENU_CopySysPopup(void)
414 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
415 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
417 if( hMenu ) {
418 POPUPMENU* menu = MENU_GetMenu(hMenu);
419 menu->wFlags |= MF_SYSMENU | MF_POPUP;
420 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
422 else
423 ERR("Unable to load default system menu\n" );
425 TRACE("returning %p.\n", hMenu );
427 return hMenu;
431 /**********************************************************************
432 * MENU_GetSysMenu
434 * Create a copy of the system menu. System menu in Windows is
435 * a special menu bar with the single entry - system menu popup.
436 * This popup is presented to the outside world as a "system menu".
437 * However, the real system menu handle is sometimes seen in the
438 * WM_MENUSELECT parameters (and Word 6 likes it this way).
440 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
442 HMENU hMenu;
444 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
445 if ((hMenu = CreateMenu()))
447 POPUPMENU *menu = MENU_GetMenu(hMenu);
448 menu->wFlags = MF_SYSMENU;
449 menu->hWnd = WIN_GetFullHandle( hWnd );
450 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
452 if (!hPopupMenu)
453 hPopupMenu = MENU_CopySysPopup();
455 if (hPopupMenu)
457 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
458 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
460 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
461 (UINT_PTR)hPopupMenu, NULL );
463 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
464 menu->items[0].fState = 0;
465 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
467 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
468 return hMenu;
470 DestroyMenu( hMenu );
472 ERR("failed to load system menu!\n");
473 return 0;
477 /***********************************************************************
478 * MENU_InitSysMenuPopup
480 * Grey the appropriate items in System menu.
482 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
484 BOOL gray;
486 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
487 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
488 gray = ((style & WS_MAXIMIZE) != 0);
489 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
490 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
491 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
492 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
493 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
494 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
495 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
496 gray = (clsStyle & CS_NOCLOSE) != 0;
498 /* The menu item must keep its state if it's disabled */
499 if(gray)
500 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
504 /******************************************************************************
506 * UINT MENU_GetStartOfNextColumn(
507 * HMENU hMenu )
509 *****************************************************************************/
511 static UINT MENU_GetStartOfNextColumn(
512 HMENU hMenu )
514 POPUPMENU *menu = MENU_GetMenu(hMenu);
515 UINT i;
517 if(!menu)
518 return NO_SELECTED_ITEM;
520 i = menu->FocusedItem + 1;
521 if( i == NO_SELECTED_ITEM )
522 return i;
524 for( ; i < menu->nItems; ++i ) {
525 if (menu->items[i].fType & MF_MENUBARBREAK)
526 return i;
529 return NO_SELECTED_ITEM;
533 /******************************************************************************
535 * UINT MENU_GetStartOfPrevColumn(
536 * HMENU hMenu )
538 *****************************************************************************/
540 static UINT MENU_GetStartOfPrevColumn(
541 HMENU hMenu )
543 POPUPMENU *menu = MENU_GetMenu(hMenu);
544 UINT i;
546 if( !menu )
547 return NO_SELECTED_ITEM;
549 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
550 return NO_SELECTED_ITEM;
552 /* Find the start of the column */
554 for(i = menu->FocusedItem; i != 0 &&
555 !(menu->items[i].fType & MF_MENUBARBREAK);
556 --i); /* empty */
558 if(i == 0)
559 return NO_SELECTED_ITEM;
561 for(--i; i != 0; --i) {
562 if (menu->items[i].fType & MF_MENUBARBREAK)
563 break;
566 TRACE("ret %d.\n", i );
568 return i;
573 /***********************************************************************
574 * MENU_FindItem
576 * Find a menu item. Return a pointer on the item, and modifies *hmenu
577 * in case the item was in a sub-menu.
579 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
581 POPUPMENU *menu;
582 MENUITEM *fallback = NULL;
583 UINT i;
585 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
586 if (wFlags & MF_BYPOSITION)
588 if (*nPos >= menu->nItems) return NULL;
589 return &menu->items[*nPos];
591 else
593 MENUITEM *item = menu->items;
594 for (i = 0; i < menu->nItems; i++, item++)
596 if (item->fType & MF_POPUP)
598 HMENU hsubmenu = item->hSubMenu;
599 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
600 if (subitem)
602 *hmenu = hsubmenu;
603 return subitem;
605 if ((UINT_PTR)item->hSubMenu == *nPos)
606 fallback = item; /* fallback to this item if nothing else found */
608 else if (item->wID == *nPos)
610 *nPos = i;
611 return item;
615 return fallback;
618 /***********************************************************************
619 * MENU_FindSubMenu
621 * Find a Sub menu. Return the position of the submenu, and modifies
622 * *hmenu in case it is found in another sub-menu.
623 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
625 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
627 POPUPMENU *menu;
628 UINT i;
629 MENUITEM *item;
630 if (((*hmenu)==(HMENU)0xffff) ||
631 (!(menu = MENU_GetMenu(*hmenu))))
632 return NO_SELECTED_ITEM;
633 item = menu->items;
634 for (i = 0; i < menu->nItems; i++, item++) {
635 if(!(item->fType & MF_POPUP)) continue;
636 if (item->hSubMenu == hSubTarget) {
637 return i;
639 else {
640 HMENU hsubmenu = item->hSubMenu;
641 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
642 if (pos != NO_SELECTED_ITEM) {
643 *hmenu = hsubmenu;
644 return pos;
648 return NO_SELECTED_ITEM;
651 /***********************************************************************
652 * MENU_FreeItemData
654 static void MENU_FreeItemData( MENUITEM* item )
656 /* delete text */
657 HeapFree( GetProcessHeap(), 0, item->text );
660 /***********************************************************************
661 * MENU_AdjustMenuItemRect
663 * Adjust menu item rectangle according to scrolling state.
665 static void
666 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
668 if (menu->bScrolling)
670 UINT arrow_bitmap_width, arrow_bitmap_height;
671 BITMAP bmp;
673 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
674 arrow_bitmap_width = bmp.bmWidth;
675 arrow_bitmap_height = bmp.bmHeight;
676 rect->top += arrow_bitmap_height - menu->nScrollPos;
677 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
682 /***********************************************************************
683 * MENU_FindItemByCoords
685 * Find the item at the specified coordinates (screen coords). Does
686 * not work for child windows and therefore should not be called for
687 * an arbitrary system menu.
689 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
690 POINT pt, UINT *pos )
692 MENUITEM *item;
693 UINT i;
694 RECT wrect;
695 RECT rect;
697 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
698 pt.x -= wrect.left;pt.y -= wrect.top;
699 item = menu->items;
700 for (i = 0; i < menu->nItems; i++, item++)
702 rect = item->rect;
703 MENU_AdjustMenuItemRect(menu, &rect);
704 if ((pt.x >= rect.left) && (pt.x < rect.right) &&
705 (pt.y >= rect.top) && (pt.y < rect.bottom))
707 if (pos) *pos = i;
708 return item;
711 return NULL;
715 /***********************************************************************
716 * MENU_FindItemByKey
718 * Find the menu item selected by a key press.
719 * Return item id, -1 if none, -2 if we should close the menu.
721 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
722 WCHAR key, BOOL forceMenuChar )
724 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
726 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
728 if (hmenu)
730 POPUPMENU *menu = MENU_GetMenu( hmenu );
731 MENUITEM *item = menu->items;
732 LRESULT menuchar;
734 if( !forceMenuChar )
736 UINT i;
738 for (i = 0; i < menu->nItems; i++, item++)
740 if( item->text)
742 WCHAR *p = item->text - 2;
745 p = strchrW (p + 2, '&');
747 while (p != NULL && p [1] == '&');
748 if (p && (toupperW(p[1]) == toupperW(key))) return i;
752 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
753 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
754 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
755 if (HIWORD(menuchar) == 1) return (UINT)(-2);
757 return (UINT)(-1);
761 /***********************************************************************
762 * MENU_GetBitmapItemSize
764 * Get the size of a bitmap item.
766 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
767 HWND hwndOwner)
769 BITMAP bm;
770 HBITMAP bmp = lpitem->hbmpItem;
772 size->cx = size->cy = 0;
774 /* check if there is a magic menu item associated with this item */
775 switch( (INT_PTR) bmp )
777 case (INT_PTR)HBMMENU_CALLBACK:
779 MEASUREITEMSTRUCT measItem;
780 measItem.CtlType = ODT_MENU;
781 measItem.CtlID = 0;
782 measItem.itemID = lpitem->wID;
783 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
784 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
785 measItem.itemData = lpitem->dwItemData;
786 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
787 size->cx = measItem.itemWidth;
788 size->cy = measItem.itemHeight;
789 return;
791 break;
792 case (INT_PTR)HBMMENU_SYSTEM:
793 if (lpitem->dwItemData)
795 bmp = (HBITMAP)lpitem->dwItemData;
796 break;
798 /* fall through */
799 case (INT_PTR)HBMMENU_MBAR_RESTORE:
800 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
801 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
802 case (INT_PTR)HBMMENU_MBAR_CLOSE:
803 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
804 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
805 size->cy = size->cx;
806 return;
807 case (INT_PTR)HBMMENU_POPUP_CLOSE:
808 case (INT_PTR)HBMMENU_POPUP_RESTORE:
809 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
810 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
811 FIXME("Magic %p not implemented\n", bmp );
812 return;
814 if (GetObjectW(bmp, sizeof(bm), &bm ))
816 size->cx = bm.bmWidth;
817 size->cy = bm.bmHeight;
821 /***********************************************************************
822 * MENU_DrawBitmapItem
824 * Draw a bitmap item.
826 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
827 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
829 BITMAP bm;
830 DWORD rop;
831 HDC hdcMem;
832 HBITMAP bmp;
833 int w = rect->right - rect->left;
834 int h = rect->bottom - rect->top;
835 int bmp_xoffset = 0;
836 int left, top;
837 HBITMAP hbmToDraw = lpitem->hbmpItem;
838 bmp = hbmToDraw;
840 /* Check if there is a magic menu item associated with this item */
841 if (IS_MAGIC_BITMAP(hbmToDraw))
843 UINT flags = 0;
844 RECT r;
846 switch((INT_PTR)hbmToDraw)
848 case (INT_PTR)HBMMENU_SYSTEM:
849 if (lpitem->dwItemData)
851 bmp = (HBITMAP)lpitem->dwItemData;
852 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
854 else
856 static HBITMAP hBmpSysMenu;
858 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
859 bmp = hBmpSysMenu;
860 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
861 /* only use right half of the bitmap */
862 bmp_xoffset = bm.bmWidth / 2;
863 bm.bmWidth -= bmp_xoffset;
865 goto got_bitmap;
866 case (INT_PTR)HBMMENU_MBAR_RESTORE:
867 flags = DFCS_CAPTIONRESTORE;
868 break;
869 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
870 flags = DFCS_CAPTIONMIN;
871 break;
872 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
873 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
874 break;
875 case (INT_PTR)HBMMENU_MBAR_CLOSE:
876 flags = DFCS_CAPTIONCLOSE;
877 break;
878 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
879 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
880 break;
881 case (INT_PTR)HBMMENU_CALLBACK:
883 DRAWITEMSTRUCT drawItem;
884 drawItem.CtlType = ODT_MENU;
885 drawItem.CtlID = 0;
886 drawItem.itemID = lpitem->wID;
887 drawItem.itemAction = odaction;
888 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
889 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
890 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
891 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
892 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
893 drawItem.hwndItem = (HWND)hmenu;
894 drawItem.hDC = hdc;
895 drawItem.itemData = lpitem->dwItemData;
896 drawItem.rcItem = *rect;
897 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
898 return;
900 break;
901 case (INT_PTR)HBMMENU_POPUP_CLOSE:
902 case (INT_PTR)HBMMENU_POPUP_RESTORE:
903 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
904 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
905 default:
906 FIXME("Magic %p not implemented\n", hbmToDraw);
907 return;
909 r = *rect;
910 InflateRect( &r, -1, -1 );
911 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
912 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
913 return;
916 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
918 got_bitmap:
919 hdcMem = CreateCompatibleDC( hdc );
920 SelectObject( hdcMem, bmp );
922 /* handle fontsize > bitmap_height */
923 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
924 left=rect->left;
925 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
926 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
927 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
928 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
929 DeleteDC( hdcMem );
933 /***********************************************************************
934 * MENU_CalcItemSize
936 * Calculate the size of the menu item and store it in lpitem->rect.
938 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
939 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
941 WCHAR *p;
942 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
943 UINT arrow_bitmap_width;
944 BITMAP bm;
945 INT itemheight;
947 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
948 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
949 (menuBar ? " (MenuBar)" : ""));
951 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
952 arrow_bitmap_width = bm.bmWidth;
954 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
955 if( !menucharsize.cx ) {
956 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
957 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
958 * but it is unlikely an application will depend on that */
959 ODitemheight = HIWORD( GetDialogBaseUnits());
962 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
964 if (lpitem->fType & MF_OWNERDRAW)
966 MEASUREITEMSTRUCT mis;
967 mis.CtlType = ODT_MENU;
968 mis.CtlID = 0;
969 mis.itemID = lpitem->wID;
970 mis.itemData = lpitem->dwItemData;
971 mis.itemHeight = ODitemheight;
972 mis.itemWidth = 0;
973 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
974 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
975 * width of a menufont character to the width of an owner-drawn menu.
977 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
978 if (menuBar) {
979 /* under at least win95 you seem to be given a standard
980 height for the menu and the height value is ignored */
981 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
982 } else
983 lpitem->rect.bottom += mis.itemHeight;
985 TRACE("id=%04x size=%ldx%ld\n",
986 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
987 lpitem->rect.bottom-lpitem->rect.top);
988 return;
991 if (lpitem->fType & MF_SEPARATOR)
993 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
994 if( !menuBar)
995 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
996 return;
999 itemheight = 0;
1001 if (!menuBar) {
1002 if (lpitem->hbmpItem) {
1003 SIZE size;
1005 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1006 /* Keep the size of the bitmap in callback mode to be able
1007 * to draw it correctly */
1008 lpitem->bmpsize = size;
1009 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1010 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1011 lpitem->rect.right += size.cx + 2;
1012 itemheight = size.cy + 2;
1014 lpitem->rect.right += 4 + arrow_bitmap_width + menucharsize.cx;
1015 if( !(lppop->dwStyle & MNS_NOCHECK))
1016 lpitem->rect.right += check_bitmap_width;
1017 } else if (lpitem->hbmpItem) { /* menuBar */
1018 SIZE size;
1020 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1021 lpitem->bmpsize = size;
1022 lpitem->rect.right += size.cx;
1023 if( lpitem->text) lpitem->rect.right += 2;
1024 itemheight = size.cy;
1027 /* it must be a text item - unless it's the system menu */
1028 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1029 HFONT hfontOld = NULL;
1030 RECT rc = lpitem->rect;
1031 LONG txtheight, txtwidth;
1033 lpitem->xTab = 0;
1034 if ( lpitem->fState & MFS_DEFAULT ) {
1035 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1037 if (menuBar) {
1038 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1039 DT_SINGLELINE|DT_CALCRECT);
1040 lpitem->rect.right += rc.right - rc.left;
1041 itemheight = max( max( itemheight, txtheight),
1042 GetSystemMetrics( SM_CYMENU) - 1);
1043 lpitem->rect.right += 2 * menucharsize.cx;
1044 } else {
1045 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1046 RECT tmprc = rc;
1047 LONG tmpheight;
1048 int n = (int)( p - lpitem->text);
1049 /* Item contains a tab (only meaningful in popup menus) */
1050 /* get text size before the tab */
1051 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1052 DT_SINGLELINE|DT_CALCRECT);
1053 txtwidth = rc.right - rc.left;
1054 p += 1; /* advance past the Tab */
1055 /* get text size after the tab */
1056 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1057 DT_SINGLELINE|DT_CALCRECT);
1058 lpitem->xTab = menucharsize.cx +
1059 4 + check_bitmap_width + lpitem->bmpsize.cx + txtwidth;
1060 txtheight = max( txtheight, tmpheight);
1061 txtwidth += menucharsize.cx + /* space for the tab */
1062 tmprc.right - tmprc.left; /* space for the short cut */
1063 } else {
1064 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1065 DT_SINGLELINE|DT_CALCRECT);
1066 txtwidth = rc.right - rc.left;
1067 if (strchrW( lpitem->text, '\b' ))
1068 lpitem->rect.right += menucharsize.cx;
1069 lpitem->xTab = 4 + check_bitmap_width + lpitem->bmpsize.cx +
1070 txtwidth;
1072 if( (lppop->dwStyle & MNS_NOCHECK))
1073 lpitem->xTab -= check_bitmap_width;
1074 lpitem->rect.right += 2 + txtwidth;
1075 itemheight = max( itemheight,
1076 max( txtheight + 2, menucharsize.cy + 4));
1078 if (hfontOld) SelectObject (hdc, hfontOld);
1079 } else if( menuBar) {
1080 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1082 lpitem->rect.bottom += itemheight;
1083 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1087 /***********************************************************************
1088 * MENU_GetMaxPopupHeight
1090 static UINT
1091 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1093 if (lppop->cyMax)
1094 return lppop->cyMax;
1095 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1099 /***********************************************************************
1100 * MENU_PopupMenuCalcSize
1102 * Calculate the size of a popup menu.
1104 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1106 MENUITEM *lpitem;
1107 HDC hdc;
1108 int start, i;
1109 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1111 lppop->Width = lppop->Height = 0;
1112 if (lppop->nItems == 0) return;
1113 hdc = GetDC( 0 );
1115 SelectObject( hdc, get_menu_font(FALSE));
1117 start = 0;
1118 maxX = 2 + 1;
1120 lppop->maxBmpSize.cx = 0;
1121 lppop->maxBmpSize.cy = 0;
1123 while (start < lppop->nItems)
1125 lpitem = &lppop->items[start];
1126 orgX = maxX;
1127 if( lpitem->fType & MF_MENUBREAK)
1128 orgX += MENU_COL_SPACE;
1129 orgY = MENU_TOP_MARGIN;
1131 maxTab = maxTabWidth = 0;
1132 /* Parse items until column break or end of menu */
1133 for (i = start; i < lppop->nItems; i++, lpitem++)
1135 if ((i != start) &&
1136 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1138 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1140 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1141 maxX = max( maxX, lpitem->rect.right );
1142 orgY = lpitem->rect.bottom;
1143 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1145 maxTab = max( maxTab, lpitem->xTab );
1146 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1150 /* Finish the column (set all items to the largest width found) */
1151 maxX = max( maxX, maxTab + maxTabWidth );
1152 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1154 lpitem->rect.right = maxX;
1155 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1156 lpitem->xTab = maxTab;
1159 lppop->Height = max( lppop->Height, orgY );
1162 lppop->Width = maxX;
1164 /* space for 3d border */
1165 lppop->Height += MENU_BOTTOM_MARGIN;
1166 lppop->Width += 2;
1168 /* Adjust popup height if it exceeds maximum */
1169 maxHeight = MENU_GetMaxPopupHeight(lppop);
1170 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1171 if (lppop->Height >= maxHeight)
1173 lppop->Height = maxHeight;
1174 lppop->bScrolling = TRUE;
1176 else
1178 lppop->bScrolling = FALSE;
1181 ReleaseDC( 0, hdc );
1185 /***********************************************************************
1186 * MENU_MenuBarCalcSize
1188 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1189 * height is off by 1 pixel which causes lengthy window relocations when
1190 * active document window is maximized/restored.
1192 * Calculate the size of the menu bar.
1194 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1195 LPPOPUPMENU lppop, HWND hwndOwner )
1197 MENUITEM *lpitem;
1198 int start, i, orgX, orgY, maxY, helpPos;
1200 if ((lprect == NULL) || (lppop == NULL)) return;
1201 if (lppop->nItems == 0) return;
1202 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1203 lppop->Width = lprect->right - lprect->left;
1204 lppop->Height = 0;
1205 maxY = lprect->top+1;
1206 start = 0;
1207 helpPos = -1;
1208 lppop->maxBmpSize.cx = 0;
1209 lppop->maxBmpSize.cy = 0;
1210 while (start < lppop->nItems)
1212 lpitem = &lppop->items[start];
1213 orgX = lprect->left;
1214 orgY = maxY;
1216 /* Parse items until line break or end of menu */
1217 for (i = start; i < lppop->nItems; i++, lpitem++)
1219 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1220 if ((i != start) &&
1221 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1223 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1224 debug_print_menuitem (" item: ", lpitem, "");
1225 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1227 if (lpitem->rect.right > lprect->right)
1229 if (i != start) break;
1230 else lpitem->rect.right = lprect->right;
1232 maxY = max( maxY, lpitem->rect.bottom );
1233 orgX = lpitem->rect.right;
1236 /* Finish the line (set all items to the largest height found) */
1237 while (start < i) lppop->items[start++].rect.bottom = maxY;
1240 lprect->bottom = maxY;
1241 lppop->Height = lprect->bottom - lprect->top;
1243 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1244 /* the last item (if several lines, only move the last line) */
1245 lpitem = &lppop->items[lppop->nItems-1];
1246 orgY = lpitem->rect.top;
1247 orgX = lprect->right;
1248 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1249 if ( (helpPos==-1) || (helpPos>i) )
1250 break; /* done */
1251 if (lpitem->rect.top != orgY) break; /* Other line */
1252 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1253 lpitem->rect.left += orgX - lpitem->rect.right;
1254 lpitem->rect.right = orgX;
1255 orgX = lpitem->rect.left;
1260 /***********************************************************************
1261 * MENU_DrawScrollArrows
1263 * Draw scroll arrows.
1265 static void
1266 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1268 HDC hdcMem = CreateCompatibleDC(hdc);
1269 HBITMAP hOrigBitmap;
1270 UINT arrow_bitmap_width, arrow_bitmap_height;
1271 BITMAP bmp;
1272 RECT rect;
1274 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1275 arrow_bitmap_width = bmp.bmWidth;
1276 arrow_bitmap_height = bmp.bmHeight;
1279 if (lppop->nScrollPos)
1280 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1281 else
1282 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1283 rect.left = 0;
1284 rect.top = 0;
1285 rect.right = lppop->Width;
1286 rect.bottom = arrow_bitmap_height;
1287 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1288 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1289 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1290 rect.top = lppop->Height - arrow_bitmap_height;
1291 rect.bottom = lppop->Height;
1292 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1293 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1294 SelectObject(hdcMem, get_down_arrow_bitmap());
1295 else
1296 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1297 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1298 lppop->Height - arrow_bitmap_height,
1299 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1300 SelectObject(hdcMem, hOrigBitmap);
1301 DeleteDC(hdcMem);
1305 /***********************************************************************
1306 * draw_popup_arrow
1308 * Draws the popup-menu arrow.
1310 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1311 UINT arrow_bitmap_height)
1313 HDC hdcMem = CreateCompatibleDC( hdc );
1314 HBITMAP hOrigBitmap;
1316 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1317 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1318 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1319 arrow_bitmap_width, arrow_bitmap_height,
1320 hdcMem, 0, 0, SRCCOPY );
1321 SelectObject( hdcMem, hOrigBitmap );
1322 DeleteDC( hdcMem );
1324 /***********************************************************************
1325 * MENU_DrawMenuItem
1327 * Draw a single menu item.
1329 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1330 UINT height, BOOL menuBar, UINT odaction )
1332 RECT rect;
1333 BOOL flat_menu = FALSE;
1334 int bkgnd;
1335 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1336 POPUPMENU *menu = MENU_GetMenu(hmenu);
1337 RECT bmprc;
1339 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1341 if (!menuBar) {
1342 BITMAP bmp;
1343 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1344 arrow_bitmap_width = bmp.bmWidth;
1345 arrow_bitmap_height = bmp.bmHeight;
1348 if (lpitem->fType & MF_SYSMENU)
1350 if( !IsIconic(hwnd) )
1351 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1352 return;
1355 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1356 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1358 /* Setup colors */
1360 if (lpitem->fState & MF_HILITE)
1362 if(menuBar && !flat_menu) {
1363 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1364 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1365 } else {
1366 if(lpitem->fState & MF_GRAYED)
1367 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1368 else
1369 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1370 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1373 else
1375 if (lpitem->fState & MF_GRAYED)
1376 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1377 else
1378 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1379 SetBkColor( hdc, GetSysColor( bkgnd ) );
1382 if (lpitem->fType & MF_OWNERDRAW)
1385 ** Experimentation under Windows reveals that an owner-drawn
1386 ** menu is given the rectangle which includes the space it requested
1387 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1388 ** and a popup-menu arrow. This is the value of lpitem->rect.
1389 ** Windows will leave all drawing to the application except for
1390 ** the popup-menu arrow. Windows always draws that itself, after
1391 ** the menu owner has finished drawing.
1393 DRAWITEMSTRUCT dis;
1395 dis.CtlType = ODT_MENU;
1396 dis.CtlID = 0;
1397 dis.itemID = lpitem->wID;
1398 dis.itemData = lpitem->dwItemData;
1399 dis.itemState = 0;
1400 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1401 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1402 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1403 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1404 dis.hwndItem = (HWND)hmenu;
1405 dis.hDC = hdc;
1406 dis.rcItem = lpitem->rect;
1407 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &dis.rcItem);
1408 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1409 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1410 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1411 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1412 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1413 /* Draw the popup-menu arrow */
1414 if (lpitem->fType & MF_POPUP)
1415 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1416 arrow_bitmap_height);
1417 return;
1420 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1422 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1424 rect = lpitem->rect;
1425 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1427 if (lpitem->fState & MF_HILITE)
1429 if (flat_menu)
1431 InflateRect (&rect, -1, -1);
1432 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1433 InflateRect (&rect, 1, 1);
1434 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1436 else
1438 if(menuBar)
1439 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1440 else
1441 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1444 else
1445 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1447 SetBkMode( hdc, TRANSPARENT );
1449 /* vertical separator */
1450 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1452 HPEN oldPen;
1453 RECT rc = rect;
1455 rc.top = 3;
1456 rc.bottom = height - 3;
1457 if (flat_menu)
1459 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1460 MoveToEx( hdc, rc.left, rc.top, NULL );
1461 LineTo( hdc, rc.left, rc.bottom );
1462 SelectObject( hdc, oldPen );
1464 else
1465 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1468 /* horizontal separator */
1469 if (lpitem->fType & MF_SEPARATOR)
1471 HPEN oldPen;
1472 RECT rc = rect;
1474 rc.left++;
1475 rc.right--;
1476 rc.top = ( rc.top + rc.bottom) / 2;
1477 if (flat_menu)
1479 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1480 MoveToEx( hdc, rc.left, rc.top, NULL );
1481 LineTo( hdc, rc.right, rc.top );
1482 SelectObject( hdc, oldPen );
1484 else
1485 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1486 return;
1489 /* helper lines for debugging */
1490 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1491 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1492 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1493 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1496 if (lpitem->hbmpItem) {
1497 /* calculate the bitmap rectangle in coordinates relative
1498 * to the item rectangle */
1499 if( menuBar) {
1500 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1501 bmprc.left = 3;
1502 else
1503 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1504 } else {
1505 bmprc.left = 4;
1506 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1507 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1509 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1510 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1511 bmprc.top = 0;
1512 else
1513 bmprc.top = (lpitem->rect.bottom - lpitem->rect.top -
1514 lpitem->bmpsize.cy) / 2;
1515 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1518 if (!menuBar)
1520 HBITMAP bm;
1521 INT y = rect.top + rect.bottom;
1522 RECT rc = rect;
1523 int checked = FALSE;
1524 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1525 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1526 /* Draw the check mark
1528 * FIXME:
1529 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1531 if( !(menu->dwStyle & MNS_NOCHECK)) {
1532 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1533 lpitem->hUnCheckBit;
1534 if (bm) /* we have a custom bitmap */
1536 HDC hdcMem = CreateCompatibleDC( hdc );
1538 SelectObject( hdcMem, bm );
1539 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1540 check_bitmap_width, check_bitmap_height,
1541 hdcMem, 0, 0, SRCCOPY );
1542 DeleteDC( hdcMem );
1543 checked = TRUE;
1545 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1547 RECT r;
1548 HBITMAP bm = CreateBitmap( check_bitmap_width,
1549 check_bitmap_height, 1, 1, NULL );
1550 HDC hdcMem = CreateCompatibleDC( hdc );
1552 SelectObject( hdcMem, bm );
1553 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1554 DrawFrameControl( hdcMem, &r, DFC_MENU,
1555 (lpitem->fType & MFT_RADIOCHECK) ?
1556 DFCS_MENUBULLET : DFCS_MENUCHECK );
1557 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1558 hdcMem, 0, 0, SRCCOPY );
1559 DeleteDC( hdcMem );
1560 DeleteObject( bm );
1561 checked = TRUE;
1564 if( lpitem->hbmpItem &&
1565 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1566 POINT origorg;
1567 /* some applications make this assumption on the DC's origin */
1568 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1569 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1570 odaction, FALSE);
1571 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1573 /* Draw the popup-menu arrow */
1574 if (lpitem->fType & MF_POPUP)
1575 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1576 arrow_bitmap_height);
1577 rect.left += 4;
1578 if( !(menu->dwStyle & MNS_NOCHECK))
1579 rect.left += check_bitmap_width;
1580 rect.right -= arrow_bitmap_width;
1582 else if( lpitem->hbmpItem)
1583 { /* Draw the bitmap */
1584 POINT origorg;
1586 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1587 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1588 odaction, menuBar);
1589 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1591 /* process text if present */
1592 if (lpitem->text)
1594 register int i;
1595 HFONT hfontOld = 0;
1597 UINT uFormat = (menuBar) ?
1598 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1599 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1601 if( !(menu->dwStyle & MNS_CHECKORBMP))
1602 rect.left += menu->maxBmpSize.cx;
1604 if ( lpitem->fState & MFS_DEFAULT )
1606 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1609 if (menuBar) {
1610 if( lpitem->hbmpItem)
1611 rect.left += lpitem->bmpsize.cx;
1612 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1613 rect.left += menucharsize.cx;
1614 rect.right -= menucharsize.cx;
1617 for (i = 0; lpitem->text[i]; i++)
1618 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1619 break;
1621 if(lpitem->fState & MF_GRAYED)
1623 if (!(lpitem->fState & MF_HILITE) )
1625 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1626 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1627 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1628 --rect.left; --rect.top; --rect.right; --rect.bottom;
1630 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1633 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1635 /* paint the shortcut text */
1636 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1638 if (lpitem->text[i] == '\t')
1640 rect.left = lpitem->xTab;
1641 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1643 else
1645 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1648 if(lpitem->fState & MF_GRAYED)
1650 if (!(lpitem->fState & MF_HILITE) )
1652 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1653 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1654 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1655 --rect.left; --rect.top; --rect.right; --rect.bottom;
1657 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1659 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1662 if (hfontOld)
1663 SelectObject (hdc, hfontOld);
1668 /***********************************************************************
1669 * MENU_DrawPopupMenu
1671 * Paint a popup menu.
1673 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1675 HBRUSH hPrevBrush = 0;
1676 RECT rect;
1678 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1680 GetClientRect( hwnd, &rect );
1682 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1683 && (SelectObject( hdc, get_menu_font(FALSE))))
1685 HPEN hPrevPen;
1687 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1689 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1690 if( hPrevPen )
1692 POPUPMENU *menu;
1693 BOOL flat_menu = FALSE;
1695 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1696 if (flat_menu)
1697 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1698 else
1699 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1701 menu = MENU_GetMenu( hmenu );
1703 /* draw menu items */
1704 if (menu && menu->nItems)
1706 MENUITEM *item;
1707 UINT u;
1709 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1710 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1711 menu->Height, FALSE, ODA_DRAWENTIRE );
1715 /* draw scroll arrows */
1716 if (menu->bScrolling)
1717 MENU_DrawScrollArrows(menu, hdc);
1718 } else
1720 SelectObject( hdc, hPrevBrush );
1725 /***********************************************************************
1726 * MENU_DrawMenuBar
1728 * Paint a menu bar. Returns the height of the menu bar.
1729 * called from [windows/nonclient.c]
1731 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1732 BOOL suppress_draw)
1734 LPPOPUPMENU lppop;
1735 HFONT hfontOld = 0;
1736 HMENU hMenu = GetMenu(hwnd);
1738 lppop = MENU_GetMenu( hMenu );
1739 if (lppop == NULL || lprect == NULL)
1741 return GetSystemMetrics(SM_CYMENU);
1744 if (suppress_draw)
1746 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1748 if (lppop->Height == 0)
1749 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1751 lprect->bottom = lprect->top + lppop->Height;
1753 if (hfontOld) SelectObject( hDC, hfontOld);
1754 return lppop->Height;
1756 else
1757 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1761 /***********************************************************************
1762 * MENU_ShowPopup
1764 * Display a popup menu.
1766 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1767 INT x, INT y, INT xanchor, INT yanchor )
1769 POPUPMENU *menu;
1770 UINT width, height;
1772 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1773 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1775 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1776 if (menu->FocusedItem != NO_SELECTED_ITEM)
1778 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1779 menu->FocusedItem = NO_SELECTED_ITEM;
1782 /* store the owner for DrawItem */
1783 menu->hwndOwner = hwndOwner;
1785 menu->nScrollPos = 0;
1786 MENU_PopupMenuCalcSize( menu, hwndOwner );
1788 /* adjust popup menu pos so that it fits within the desktop */
1790 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1791 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1793 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1795 if( xanchor )
1796 x -= width - xanchor;
1797 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1798 x = GetSystemMetrics(SM_CXSCREEN) - width;
1800 if( x < 0 ) x = 0;
1802 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1804 if( yanchor )
1805 y -= height + yanchor;
1806 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1807 y = GetSystemMetrics(SM_CYSCREEN) - height;
1809 if( y < 0 ) y = 0;
1811 /* NOTE: In Windows, top menu popup is not owned. */
1812 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1813 WS_POPUP, x, y, width, height,
1814 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1815 (LPVOID)hmenu );
1816 if( !menu->hWnd ) return FALSE;
1817 if (!top_popup) top_popup = menu->hWnd;
1819 /* Display the window */
1821 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1822 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1823 UpdateWindow( menu->hWnd );
1824 return TRUE;
1828 /***********************************************************************
1829 * MENU_EnsureMenuItemVisible
1831 static void
1832 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1834 if (lppop->bScrolling)
1836 MENUITEM *item = &lppop->items[wIndex];
1837 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1838 UINT nOldPos = lppop->nScrollPos;
1839 RECT rc;
1840 UINT arrow_bitmap_height;
1841 BITMAP bmp;
1843 GetClientRect(lppop->hWnd, &rc);
1845 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1846 arrow_bitmap_height = bmp.bmHeight;
1848 rc.top += arrow_bitmap_height;
1849 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1851 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1852 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1855 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1856 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1857 MENU_DrawScrollArrows(lppop, hdc);
1859 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1861 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1862 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1863 MENU_DrawScrollArrows(lppop, hdc);
1869 /***********************************************************************
1870 * MENU_SelectItem
1872 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1873 BOOL sendMenuSelect, HMENU topmenu )
1875 LPPOPUPMENU lppop;
1876 HDC hdc;
1878 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1880 lppop = MENU_GetMenu( hmenu );
1881 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1883 if (lppop->FocusedItem == wIndex) return;
1884 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1885 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1886 if (!top_popup) top_popup = lppop->hWnd;
1888 SelectObject( hdc, get_menu_font(FALSE));
1890 /* Clear previous highlighted item */
1891 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1893 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1894 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1895 lppop->Height, !(lppop->wFlags & MF_POPUP),
1896 ODA_SELECT );
1899 /* Highlight new item (if any) */
1900 lppop->FocusedItem = wIndex;
1901 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1903 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1904 lppop->items[wIndex].fState |= MF_HILITE;
1905 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1906 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1907 &lppop->items[wIndex], lppop->Height,
1908 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1910 if (sendMenuSelect)
1912 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1913 SendMessageW( hwndOwner, WM_MENUSELECT,
1914 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1915 ip->fType | ip->fState |
1916 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1919 else if (sendMenuSelect) {
1920 if(topmenu){
1921 int pos;
1922 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1923 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1924 MENUITEM *ip = &ptm->items[pos];
1925 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1926 ip->fType | ip->fState |
1927 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1931 ReleaseDC( lppop->hWnd, hdc );
1935 /***********************************************************************
1936 * MENU_MoveSelection
1938 * Moves currently selected item according to the offset parameter.
1939 * If there is no selection then it should select the last item if
1940 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1942 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1944 INT i;
1945 POPUPMENU *menu;
1947 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1949 menu = MENU_GetMenu( hmenu );
1950 if ((!menu) || (!menu->items)) return;
1952 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1954 if( menu->nItems == 1 ) return; else
1955 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1956 ; i += offset)
1957 if (!(menu->items[i].fType & MF_SEPARATOR))
1959 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1960 return;
1964 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1965 i >= 0 && i < menu->nItems ; i += offset)
1966 if (!(menu->items[i].fType & MF_SEPARATOR))
1968 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1969 return;
1974 /**********************************************************************
1975 * MENU_SetItemData
1977 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1978 * ModifyMenu().
1980 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1981 LPCWSTR str )
1983 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1984 TRACE("flags=%x str=%p\n", flags, str);
1986 if (IS_STRING_ITEM(flags))
1988 LPWSTR prevText = item->text;
1989 if (!str)
1991 flags |= MF_SEPARATOR;
1992 item->text = NULL;
1994 else
1996 LPWSTR text;
1997 /* Item beginning with a backspace is a help item */
1998 if (*str == '\b')
2000 flags |= MF_HELP;
2001 str++;
2003 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2004 return FALSE;
2005 strcpyW( text, str );
2006 item->text = text;
2008 item->hbmpItem = NULL;
2009 HeapFree( GetProcessHeap(), 0, prevText );
2011 else if(( flags & MFT_BITMAP)) {
2012 item->hbmpItem = HBITMAP_32(LOWORD(str));
2013 /* setting bitmap clears text */
2014 HeapFree( GetProcessHeap(), 0, item->text );
2015 item->text = NULL;
2018 if (flags & MF_OWNERDRAW)
2019 item->dwItemData = (DWORD_PTR)str;
2020 else
2021 item->dwItemData = 0;
2023 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2024 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2026 if (flags & MF_POPUP)
2028 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2029 if (menu) menu->wFlags |= MF_POPUP;
2030 else
2032 item->wID = 0;
2033 item->hSubMenu = 0;
2034 item->fType = 0;
2035 item->fState = 0;
2036 return FALSE;
2040 item->wID = id;
2041 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2043 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2044 flags |= MF_POPUP; /* keep popup */
2046 item->fType = flags & TYPE_MASK;
2047 item->fState = (flags & STATE_MASK) &
2048 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
2050 /* Don't call SetRectEmpty here! */
2052 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2053 return TRUE;
2057 /**********************************************************************
2058 * MENU_InsertItem
2060 * Insert (allocate) a new item into a menu.
2062 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2064 MENUITEM *newItems;
2065 POPUPMENU *menu;
2067 if (!(menu = MENU_GetMenu(hMenu)))
2068 return NULL;
2070 /* Find where to insert new item */
2072 if (flags & MF_BYPOSITION) {
2073 if (pos > menu->nItems)
2074 pos = menu->nItems;
2075 } else {
2076 if (!MENU_FindItem( &hMenu, &pos, flags ))
2077 pos = menu->nItems;
2078 else {
2079 if (!(menu = MENU_GetMenu( hMenu )))
2080 return NULL;
2084 /* Create new items array */
2086 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2087 if (!newItems)
2089 WARN("allocation failed\n" );
2090 return NULL;
2092 if (menu->nItems > 0)
2094 /* Copy the old array into the new one */
2095 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2096 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2097 (menu->nItems-pos)*sizeof(MENUITEM) );
2098 HeapFree( GetProcessHeap(), 0, menu->items );
2100 menu->items = newItems;
2101 menu->nItems++;
2102 memset( &newItems[pos], 0, sizeof(*newItems) );
2103 menu->Height = 0; /* force size recalculate */
2104 return &newItems[pos];
2108 /**********************************************************************
2109 * MENU_ParseResource
2111 * Parse a standard menu resource and add items to the menu.
2112 * Return a pointer to the end of the resource.
2114 * NOTE: flags is equivalent to the mtOption field
2116 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2118 WORD flags, id = 0;
2119 LPCSTR str;
2123 flags = GET_WORD(res);
2124 res += sizeof(WORD);
2125 if (!(flags & MF_POPUP))
2127 id = GET_WORD(res);
2128 res += sizeof(WORD);
2130 str = res;
2131 if (!unicode) res += strlen(str) + 1;
2132 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2133 if (flags & MF_POPUP)
2135 HMENU hSubMenu = CreatePopupMenu();
2136 if (!hSubMenu) return NULL;
2137 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2138 return NULL;
2139 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2140 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2142 else /* Not a popup */
2144 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2145 else AppendMenuW( hMenu, flags, id,
2146 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2148 } while (!(flags & MF_END));
2149 return res;
2153 /**********************************************************************
2154 * MENUEX_ParseResource
2156 * Parse an extended menu resource and add items to the menu.
2157 * Return a pointer to the end of the resource.
2159 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2161 WORD resinfo;
2162 do {
2163 MENUITEMINFOW mii;
2165 mii.cbSize = sizeof(mii);
2166 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2167 mii.fType = GET_DWORD(res);
2168 res += sizeof(DWORD);
2169 mii.fState = GET_DWORD(res);
2170 res += sizeof(DWORD);
2171 mii.wID = GET_DWORD(res);
2172 res += sizeof(DWORD);
2173 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2174 res += sizeof(WORD);
2175 /* Align the text on a word boundary. */
2176 res += (~((UINT_PTR)res - 1)) & 1;
2177 mii.dwTypeData = (LPWSTR) res;
2178 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2179 /* Align the following fields on a dword boundary. */
2180 res += (~((UINT_PTR)res - 1)) & 3;
2182 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2183 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2185 if (resinfo & 1) { /* Pop-up? */
2186 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2187 res += sizeof(DWORD);
2188 mii.hSubMenu = CreatePopupMenu();
2189 if (!mii.hSubMenu)
2190 return NULL;
2191 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2192 DestroyMenu(mii.hSubMenu);
2193 return NULL;
2195 mii.fMask |= MIIM_SUBMENU;
2196 mii.fType |= MF_POPUP;
2198 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2200 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2201 mii.wID, mii.fType);
2202 mii.fType |= MF_SEPARATOR;
2204 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2205 } while (!(resinfo & MF_END));
2206 return res;
2210 /***********************************************************************
2211 * MENU_GetSubPopup
2213 * Return the handle of the selected sub-popup menu (if any).
2215 static HMENU MENU_GetSubPopup( HMENU hmenu )
2217 POPUPMENU *menu;
2218 MENUITEM *item;
2220 menu = MENU_GetMenu( hmenu );
2222 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2224 item = &menu->items[menu->FocusedItem];
2225 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2226 return item->hSubMenu;
2227 return 0;
2231 /***********************************************************************
2232 * MENU_HideSubPopups
2234 * Hide the sub-popup menus of this menu.
2236 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2237 BOOL sendMenuSelect )
2239 POPUPMENU *menu = MENU_GetMenu( hmenu );
2241 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2243 if (menu && top_popup)
2245 HMENU hsubmenu;
2246 POPUPMENU *submenu;
2247 MENUITEM *item;
2249 if (menu->FocusedItem != NO_SELECTED_ITEM)
2251 item = &menu->items[menu->FocusedItem];
2252 if (!(item->fType & MF_POPUP) ||
2253 !(item->fState & MF_MOUSESELECT)) return;
2254 item->fState &= ~MF_MOUSESELECT;
2255 hsubmenu = item->hSubMenu;
2256 } else return;
2258 submenu = MENU_GetMenu( hsubmenu );
2259 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2260 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2261 DestroyWindow( submenu->hWnd );
2262 submenu->hWnd = 0;
2267 /***********************************************************************
2268 * MENU_ShowSubPopup
2270 * Display the sub-menu of the selected item of this menu.
2271 * Return the handle of the submenu, or hmenu if no submenu to display.
2273 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2274 BOOL selectFirst, UINT wFlags )
2276 RECT rect;
2277 POPUPMENU *menu;
2278 MENUITEM *item;
2279 HDC hdc;
2281 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2283 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2285 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2287 item = &menu->items[menu->FocusedItem];
2288 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2289 return hmenu;
2291 /* message must be sent before using item,
2292 because nearly everything may be changed by the application ! */
2294 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2295 if (!(wFlags & TPM_NONOTIFY))
2296 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2297 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2299 item = &menu->items[menu->FocusedItem];
2300 rect = item->rect;
2302 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2303 if (!(item->fState & MF_HILITE))
2305 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2306 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2308 SelectObject( hdc, get_menu_font(FALSE));
2310 item->fState |= MF_HILITE;
2311 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2312 ReleaseDC( menu->hWnd, hdc );
2314 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2315 item->rect = rect;
2317 item->fState |= MF_MOUSESELECT;
2319 if (IS_SYSTEM_MENU(menu))
2321 MENU_InitSysMenuPopup(item->hSubMenu,
2322 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2323 GetClassLongW( menu->hWnd, GCL_STYLE));
2325 NC_GetSysPopupPos( menu->hWnd, &rect );
2326 rect.top = rect.bottom;
2327 rect.right = GetSystemMetrics(SM_CXSIZE);
2328 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2330 else
2332 GetWindowRect( menu->hWnd, &rect );
2333 if (menu->wFlags & MF_POPUP)
2335 RECT rc = item->rect;
2337 MENU_AdjustMenuItemRect(menu, &rc);
2338 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2339 rect.top += rc.top;
2340 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2341 rect.bottom = rc.top - rc.bottom;
2343 else
2345 rect.left += item->rect.left;
2346 rect.top += item->rect.bottom;
2347 rect.right = item->rect.right - item->rect.left;
2348 rect.bottom = item->rect.bottom - item->rect.top;
2352 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2353 rect.left, rect.top, rect.right, rect.bottom );
2354 if (selectFirst)
2355 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2356 return item->hSubMenu;
2361 /**********************************************************************
2362 * MENU_IsMenuActive
2364 HWND MENU_IsMenuActive(void)
2366 return top_popup;
2369 /***********************************************************************
2370 * MENU_PtMenu
2372 * Walks menu chain trying to find a menu pt maps to.
2374 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2376 POPUPMENU *menu = MENU_GetMenu( hMenu );
2377 UINT item = menu->FocusedItem;
2378 HMENU ret;
2380 /* try subpopup first (if any) */
2381 ret = (item != NO_SELECTED_ITEM &&
2382 (menu->items[item].fType & MF_POPUP) &&
2383 (menu->items[item].fState & MF_MOUSESELECT))
2384 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2386 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2388 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2389 if( menu->wFlags & MF_POPUP )
2391 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2393 else if (ht == HTSYSMENU)
2394 ret = get_win_sys_menu( menu->hWnd );
2395 else if (ht == HTMENU)
2396 ret = GetMenu( menu->hWnd );
2398 return ret;
2401 /***********************************************************************
2402 * MENU_ExecFocusedItem
2404 * Execute a menu item (for instance when user pressed Enter).
2405 * Return the wID of the executed item. Otherwise, -1 indicating
2406 * that no menu item was executed;
2407 * Have to receive the flags for the TrackPopupMenu options to avoid
2408 * sending unwanted message.
2411 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2413 MENUITEM *item;
2414 POPUPMENU *menu = MENU_GetMenu( hMenu );
2416 TRACE("%p hmenu=%p\n", pmt, hMenu);
2418 if (!menu || !menu->nItems ||
2419 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2421 item = &menu->items[menu->FocusedItem];
2423 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2425 if (!(item->fType & MF_POPUP))
2427 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2429 /* If TPM_RETURNCMD is set you return the id, but
2430 do not send a message to the owner */
2431 if(!(wFlags & TPM_RETURNCMD))
2433 if( menu->wFlags & MF_SYSMENU )
2434 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2435 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2436 else
2437 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2439 return item->wID;
2442 else
2443 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2445 return -1;
2448 /***********************************************************************
2449 * MENU_SwitchTracking
2451 * Helper function for menu navigation routines.
2453 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2455 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2456 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2458 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2460 if( pmt->hTopMenu != hPtMenu &&
2461 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2463 /* both are top level menus (system and menu-bar) */
2464 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2465 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2466 pmt->hTopMenu = hPtMenu;
2468 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2469 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2473 /***********************************************************************
2474 * MENU_ButtonDown
2476 * Return TRUE if we can go on with menu tracking.
2478 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2480 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2482 if (hPtMenu)
2484 UINT id = 0;
2485 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2486 MENUITEM *item;
2488 if( IS_SYSTEM_MENU(ptmenu) )
2489 item = ptmenu->items;
2490 else
2491 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2493 if( item )
2495 if( ptmenu->FocusedItem != id )
2496 MENU_SwitchTracking( pmt, hPtMenu, id );
2498 /* If the popup menu is not already "popped" */
2499 if(!(item->fState & MF_MOUSESELECT ))
2501 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2504 return TRUE;
2506 /* Else the click was on the menu bar, finish the tracking */
2508 return FALSE;
2511 /***********************************************************************
2512 * MENU_ButtonUp
2514 * Return the value of MENU_ExecFocusedItem if
2515 * the selected item was not a popup. Else open the popup.
2516 * A -1 return value indicates that we go on with menu tracking.
2519 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2521 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2523 if (hPtMenu)
2525 UINT id = 0;
2526 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2527 MENUITEM *item;
2529 if( IS_SYSTEM_MENU(ptmenu) )
2530 item = ptmenu->items;
2531 else
2532 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2534 if( item && (ptmenu->FocusedItem == id ))
2536 if( !(item->fType & MF_POPUP) )
2537 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2539 /* If we are dealing with the top-level menu */
2540 /* and this is a click on an already "popped" item: */
2541 /* Stop the menu tracking and close the opened submenus */
2542 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2543 return 0;
2545 ptmenu->bTimeToHide = TRUE;
2547 return -1;
2551 /***********************************************************************
2552 * MENU_MouseMove
2554 * Return TRUE if we can go on with menu tracking.
2556 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2558 UINT id = NO_SELECTED_ITEM;
2559 POPUPMENU *ptmenu = NULL;
2561 if( hPtMenu )
2563 ptmenu = MENU_GetMenu( hPtMenu );
2564 if( IS_SYSTEM_MENU(ptmenu) )
2565 id = 0;
2566 else
2567 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2570 if( id == NO_SELECTED_ITEM )
2572 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2573 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2576 else if( ptmenu->FocusedItem != id )
2578 MENU_SwitchTracking( pmt, hPtMenu, id );
2579 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2581 return TRUE;
2585 /***********************************************************************
2586 * MENU_SetCapture
2588 static void MENU_SetCapture( HWND hwnd )
2590 HWND previous = 0;
2592 SERVER_START_REQ( set_capture_window )
2594 req->handle = hwnd;
2595 req->flags = CAPTURE_MENU;
2596 if (!wine_server_call_err( req ))
2598 previous = reply->previous;
2599 hwnd = reply->full_handle;
2602 SERVER_END_REQ;
2604 if (previous && previous != hwnd)
2605 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2609 /***********************************************************************
2610 * MENU_DoNextMenu
2612 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2614 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2616 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2618 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2619 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2621 MDINEXTMENU next_menu;
2622 HMENU hNewMenu;
2623 HWND hNewWnd;
2624 UINT id = 0;
2626 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2627 next_menu.hmenuNext = 0;
2628 next_menu.hwndNext = 0;
2629 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2631 TRACE("%p [%p] -> %p [%p]\n",
2632 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2634 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2636 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2637 hNewWnd = pmt->hOwnerWnd;
2638 if( IS_SYSTEM_MENU(menu) )
2640 /* switch to the menu bar */
2642 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2644 if( vk == VK_LEFT )
2646 menu = MENU_GetMenu( hNewMenu );
2647 id = menu->nItems - 1;
2650 else if (style & WS_SYSMENU )
2652 /* switch to the system menu */
2653 hNewMenu = get_win_sys_menu( hNewWnd );
2655 else return FALSE;
2657 else /* application returned a new menu to switch to */
2659 hNewMenu = next_menu.hmenuNext;
2660 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2662 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2664 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2666 if (style & WS_SYSMENU &&
2667 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2669 /* get the real system menu */
2670 hNewMenu = get_win_sys_menu(hNewWnd);
2672 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2674 /* FIXME: Not sure what to do here;
2675 * perhaps try to track hNewMenu as a popup? */
2677 TRACE(" -- got confused.\n");
2678 return FALSE;
2681 else return FALSE;
2684 if( hNewMenu != pmt->hTopMenu )
2686 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2687 FALSE, 0 );
2688 if( pmt->hCurrentMenu != pmt->hTopMenu )
2689 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2692 if( hNewWnd != pmt->hOwnerWnd )
2694 pmt->hOwnerWnd = hNewWnd;
2695 MENU_SetCapture( pmt->hOwnerWnd );
2698 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2699 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2701 return TRUE;
2703 return FALSE;
2706 /***********************************************************************
2707 * MENU_SuspendPopup
2709 * The idea is not to show the popup if the next input message is
2710 * going to hide it anyway.
2712 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2714 MSG msg;
2716 msg.hwnd = pmt->hOwnerWnd;
2718 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2719 pmt->trackFlags |= TF_SKIPREMOVE;
2721 switch( uMsg )
2723 case WM_KEYDOWN:
2724 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2725 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2727 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2728 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2729 if( msg.message == WM_KEYDOWN &&
2730 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2732 pmt->trackFlags |= TF_SUSPENDPOPUP;
2733 return TRUE;
2736 break;
2739 /* failures go through this */
2740 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2741 return FALSE;
2744 /***********************************************************************
2745 * MENU_KeyEscape
2747 * Handle a VK_ESCAPE key event in a menu.
2749 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2751 BOOL bEndMenu = TRUE;
2753 if (pmt->hCurrentMenu != pmt->hTopMenu)
2755 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2757 if (menu->wFlags & MF_POPUP)
2759 HMENU hmenutmp, hmenuprev;
2761 hmenuprev = hmenutmp = pmt->hTopMenu;
2763 /* close topmost popup */
2764 while (hmenutmp != pmt->hCurrentMenu)
2766 hmenuprev = hmenutmp;
2767 hmenutmp = MENU_GetSubPopup( hmenuprev );
2770 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2771 pmt->hCurrentMenu = hmenuprev;
2772 bEndMenu = FALSE;
2776 return bEndMenu;
2779 /***********************************************************************
2780 * MENU_KeyLeft
2782 * Handle a VK_LEFT key event in a menu.
2784 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2786 POPUPMENU *menu;
2787 HMENU hmenutmp, hmenuprev;
2788 UINT prevcol;
2790 hmenuprev = hmenutmp = pmt->hTopMenu;
2791 menu = MENU_GetMenu( hmenutmp );
2793 /* Try to move 1 column left (if possible) */
2794 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2795 NO_SELECTED_ITEM ) {
2797 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2798 prevcol, TRUE, 0 );
2799 return;
2802 /* close topmost popup */
2803 while (hmenutmp != pmt->hCurrentMenu)
2805 hmenuprev = hmenutmp;
2806 hmenutmp = MENU_GetSubPopup( hmenuprev );
2809 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2810 pmt->hCurrentMenu = hmenuprev;
2812 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2814 /* move menu bar selection if no more popups are left */
2816 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2817 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2819 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2821 /* A sublevel menu was displayed - display the next one
2822 * unless there is another displacement coming up */
2824 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2825 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2826 pmt->hTopMenu, TRUE, wFlags);
2832 /***********************************************************************
2833 * MENU_KeyRight
2835 * Handle a VK_RIGHT key event in a menu.
2837 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2839 HMENU hmenutmp;
2840 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2841 UINT nextcol;
2843 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2844 pmt->hCurrentMenu,
2845 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2846 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2848 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2850 /* If already displaying a popup, try to display sub-popup */
2852 hmenutmp = pmt->hCurrentMenu;
2853 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2855 /* if subpopup was displayed then we are done */
2856 if (hmenutmp != pmt->hCurrentMenu) return;
2859 /* Check to see if there's another column */
2860 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2861 NO_SELECTED_ITEM ) {
2862 TRACE("Going to %d.\n", nextcol );
2863 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2864 nextcol, TRUE, 0 );
2865 return;
2868 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2870 if( pmt->hCurrentMenu != pmt->hTopMenu )
2872 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2873 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2874 } else hmenutmp = 0;
2876 /* try to move to the next item */
2877 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2878 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2880 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2881 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2882 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2883 pmt->hTopMenu, TRUE, wFlags);
2887 /***********************************************************************
2888 * MENU_TrackMenu
2890 * Menu tracking code.
2892 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2893 HWND hwnd, const RECT *lprect )
2895 MSG msg;
2896 POPUPMENU *menu;
2897 BOOL fRemove;
2898 INT executedMenuId = -1;
2899 MTRACKER mt;
2900 BOOL enterIdleSent = FALSE;
2902 mt.trackFlags = 0;
2903 mt.hCurrentMenu = hmenu;
2904 mt.hTopMenu = hmenu;
2905 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2906 mt.pt.x = x;
2907 mt.pt.y = y;
2909 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2910 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2912 fEndMenu = FALSE;
2913 if (!(menu = MENU_GetMenu( hmenu )))
2915 WARN("Invalid menu handle %p\n", hmenu);
2916 SetLastError(ERROR_INVALID_MENU_HANDLE);
2917 return FALSE;
2920 if (wFlags & TPM_BUTTONDOWN)
2922 /* Get the result in order to start the tracking or not */
2923 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2924 fEndMenu = !fRemove;
2927 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2929 MENU_SetCapture( mt.hOwnerWnd );
2931 while (!fEndMenu)
2933 menu = MENU_GetMenu( mt.hCurrentMenu );
2934 if (!menu) /* sometimes happens if I do a window manager close */
2935 break;
2937 /* we have to keep the message in the queue until it's
2938 * clear that menu loop is not over yet. */
2940 for (;;)
2942 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2944 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2945 /* remove the message from the queue */
2946 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2948 else
2950 if (!enterIdleSent)
2952 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2953 enterIdleSent = TRUE;
2954 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2956 WaitMessage();
2960 /* check if EndMenu() tried to cancel us, by posting this message */
2961 if(msg.message == WM_CANCELMODE)
2963 /* we are now out of the loop */
2964 fEndMenu = TRUE;
2966 /* remove the message from the queue */
2967 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2969 /* break out of internal loop, ala ESCAPE */
2970 break;
2973 TranslateMessage( &msg );
2974 mt.pt = msg.pt;
2976 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2977 enterIdleSent=FALSE;
2979 fRemove = FALSE;
2980 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2983 * Use the mouse coordinates in lParam instead of those in the MSG
2984 * struct to properly handle synthetic messages. They are already
2985 * in screen coordinates.
2987 mt.pt.x = (short)LOWORD(msg.lParam);
2988 mt.pt.y = (short)HIWORD(msg.lParam);
2990 /* Find a menu for this mouse event */
2991 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2993 switch(msg.message)
2995 /* no WM_NC... messages in captured state */
2997 case WM_RBUTTONDBLCLK:
2998 case WM_RBUTTONDOWN:
2999 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3000 /* fall through */
3001 case WM_LBUTTONDBLCLK:
3002 case WM_LBUTTONDOWN:
3003 /* If the message belongs to the menu, removes it from the queue */
3004 /* Else, end menu tracking */
3005 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3006 fEndMenu = !fRemove;
3007 break;
3009 case WM_RBUTTONUP:
3010 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3011 /* fall through */
3012 case WM_LBUTTONUP:
3013 /* Check if a menu was selected by the mouse */
3014 if (hmenu)
3016 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3018 /* End the loop if executedMenuId is an item ID */
3019 /* or if the job was done (executedMenuId = 0). */
3020 fEndMenu = fRemove = (executedMenuId != -1);
3022 /* No menu was selected by the mouse */
3023 /* if the function was called by TrackPopupMenu, continue
3024 with the menu tracking. If not, stop it */
3025 else
3026 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3028 break;
3030 case WM_MOUSEMOVE:
3031 /* the selected menu item must be changed every time */
3032 /* the mouse moves. */
3034 if (hmenu)
3035 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3037 } /* switch(msg.message) - mouse */
3039 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3041 fRemove = TRUE; /* Keyboard messages are always removed */
3042 switch(msg.message)
3044 case WM_KEYDOWN:
3045 case WM_SYSKEYDOWN:
3046 switch(msg.wParam)
3048 case VK_MENU:
3049 fEndMenu = TRUE;
3050 break;
3052 case VK_HOME:
3053 case VK_END:
3054 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3055 NO_SELECTED_ITEM, FALSE, 0 );
3056 /* fall through */
3057 case VK_UP:
3058 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3059 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3060 break;
3062 case VK_DOWN: /* If on menu bar, pull-down the menu */
3064 menu = MENU_GetMenu( mt.hCurrentMenu );
3065 if (!(menu->wFlags & MF_POPUP))
3066 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3067 else /* otherwise try to move selection */
3068 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
3069 break;
3071 case VK_LEFT:
3072 MENU_KeyLeft( &mt, wFlags );
3073 break;
3075 case VK_RIGHT:
3076 MENU_KeyRight( &mt, wFlags );
3077 break;
3079 case VK_ESCAPE:
3080 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3081 break;
3083 case VK_F1:
3085 HELPINFO hi;
3086 hi.cbSize = sizeof(HELPINFO);
3087 hi.iContextType = HELPINFO_MENUITEM;
3088 if (menu->FocusedItem == NO_SELECTED_ITEM)
3089 hi.iCtrlId = 0;
3090 else
3091 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3092 hi.hItemHandle = hmenu;
3093 hi.dwContextId = menu->dwContextHelpID;
3094 hi.MousePos = msg.pt;
3095 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3096 break;
3099 default:
3100 break;
3102 break; /* WM_KEYDOWN */
3104 case WM_CHAR:
3105 case WM_SYSCHAR:
3107 UINT pos;
3109 if (msg.wParam == '\r' || msg.wParam == ' ')
3111 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3112 fEndMenu = (executedMenuId != -1);
3114 break;
3117 /* Hack to avoid control chars. */
3118 /* We will find a better way real soon... */
3119 if (msg.wParam < 32) break;
3121 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3122 LOWORD(msg.wParam), FALSE );
3123 if (pos == (UINT)-2) fEndMenu = TRUE;
3124 else if (pos == (UINT)-1) MessageBeep(0);
3125 else
3127 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3128 TRUE, 0 );
3129 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3130 fEndMenu = (executedMenuId != -1);
3133 break;
3134 } /* switch(msg.message) - kbd */
3136 else
3138 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3139 DispatchMessageW( &msg );
3140 continue;
3143 if (!fEndMenu) fRemove = TRUE;
3145 /* finally remove message from the queue */
3147 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3148 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3149 else mt.trackFlags &= ~TF_SKIPREMOVE;
3152 MENU_SetCapture(0); /* release the capture */
3154 /* If dropdown is still painted and the close box is clicked on
3155 then the menu will be destroyed as part of the DispatchMessage above.
3156 This will then invalidate the menu handle in mt.hTopMenu. We should
3157 check for this first. */
3158 if( IsMenu( mt.hTopMenu ) )
3160 menu = MENU_GetMenu( mt.hTopMenu );
3162 if( IsWindow( mt.hOwnerWnd ) )
3164 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3166 if (menu && (menu->wFlags & MF_POPUP))
3168 DestroyWindow( menu->hWnd );
3169 menu->hWnd = 0;
3171 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3172 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3175 /* Reset the variable for hiding menu */
3176 if( menu ) menu->bTimeToHide = FALSE;
3179 /* The return value is only used by TrackPopupMenu */
3180 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3181 if (executedMenuId == -1) executedMenuId = 0;
3182 return executedMenuId;
3185 /***********************************************************************
3186 * MENU_InitTracking
3188 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3190 POPUPMENU *menu;
3192 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3194 HideCaret(0);
3196 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3197 if (!(wFlags & TPM_NONOTIFY))
3198 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3200 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3202 if (!(wFlags & TPM_NONOTIFY))
3204 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3205 /* If an app changed/recreated menu bar entries in WM_INITMENU
3206 * menu sizes will be recalculated once the menu created/shown.
3210 /* This makes the menus of applications built with Delphi work.
3211 * It also enables menus to be displayed in more than one window,
3212 * but there are some bugs left that need to be fixed in this case.
3214 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3216 return TRUE;
3218 /***********************************************************************
3219 * MENU_ExitTracking
3221 static BOOL MENU_ExitTracking(HWND hWnd)
3223 TRACE("hwnd=%p\n", hWnd);
3225 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3226 ShowCaret(0);
3227 top_popup = 0;
3228 return TRUE;
3231 /***********************************************************************
3232 * MENU_TrackMouseMenuBar
3234 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3236 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3238 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3239 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3241 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3243 if (IsMenu(hMenu))
3245 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3246 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3247 MENU_ExitTracking(hWnd);
3252 /***********************************************************************
3253 * MENU_TrackKbdMenuBar
3255 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3257 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3259 UINT uItem = NO_SELECTED_ITEM;
3260 HMENU hTrackMenu;
3261 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3263 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3265 /* find window that has a menu */
3267 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3268 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3270 /* check if we have to track a system menu */
3272 hTrackMenu = GetMenu( hwnd );
3273 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3275 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3276 hTrackMenu = get_win_sys_menu( hwnd );
3277 uItem = 0;
3278 wParam |= HTSYSMENU; /* prevent item lookup */
3281 if (!IsMenu( hTrackMenu )) return;
3283 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3285 if( wChar && wChar != ' ' )
3287 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3288 if ( uItem >= (UINT)(-2) )
3290 if( uItem == (UINT)(-1) ) MessageBeep(0);
3291 /* schedule end of menu tracking */
3292 wFlags |= TF_ENDMENU;
3293 goto track_menu;
3297 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3299 if (wParam & HTSYSMENU)
3301 /* prevent sysmenu activation for managed windows on Alt down/up */
3302 if (GetPropA( hwnd, "__wine_x11_managed" ))
3303 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3305 else
3307 if( uItem == NO_SELECTED_ITEM )
3308 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3309 else
3310 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3313 track_menu:
3314 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3315 MENU_ExitTracking( hwnd );
3319 /**********************************************************************
3320 * TrackPopupMenu (USER32.@)
3322 * Like the win32 API, the function return the command ID only if the
3323 * flag TPM_RETURNCMD is on.
3326 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3327 INT nReserved, HWND hWnd, const RECT *lpRect )
3329 BOOL ret = FALSE;
3331 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3333 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3334 if (!(wFlags & TPM_NONOTIFY))
3335 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3337 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3338 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3339 MENU_ExitTracking(hWnd);
3341 return ret;
3344 /**********************************************************************
3345 * TrackPopupMenuEx (USER32.@)
3347 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3348 HWND hWnd, LPTPMPARAMS lpTpm )
3350 FIXME("not fully implemented\n" );
3351 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3352 lpTpm ? &lpTpm->rcExclude : NULL );
3355 /***********************************************************************
3356 * PopupMenuWndProc
3358 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3360 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3362 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3364 switch(message)
3366 case WM_CREATE:
3368 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3369 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3370 return 0;
3373 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3374 return MA_NOACTIVATE;
3376 case WM_PAINT:
3378 PAINTSTRUCT ps;
3379 BeginPaint( hwnd, &ps );
3380 MENU_DrawPopupMenu( hwnd, ps.hdc,
3381 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3382 EndPaint( hwnd, &ps );
3383 return 0;
3385 case WM_ERASEBKGND:
3386 return 1;
3388 case WM_DESTROY:
3389 /* zero out global pointer in case resident popup window was destroyed. */
3390 if (hwnd == top_popup) top_popup = 0;
3391 break;
3393 case WM_SHOWWINDOW:
3395 if( wParam )
3397 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3399 else
3400 SetWindowLongPtrW( hwnd, 0, 0 );
3401 break;
3403 case MM_SETMENUHANDLE:
3404 SetWindowLongPtrW( hwnd, 0, wParam );
3405 break;
3407 case MM_GETMENUHANDLE:
3408 return GetWindowLongPtrW( hwnd, 0 );
3410 default:
3411 return DefWindowProcW( hwnd, message, wParam, lParam );
3413 return 0;
3417 /***********************************************************************
3418 * MENU_GetMenuBarHeight
3420 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3422 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3423 INT orgX, INT orgY )
3425 HDC hdc;
3426 RECT rectBar;
3427 LPPOPUPMENU lppop;
3429 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3431 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3433 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3434 SelectObject( hdc, get_menu_font(FALSE));
3435 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3436 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3437 ReleaseDC( hwnd, hdc );
3438 return lppop->Height;
3442 /*******************************************************************
3443 * ChangeMenuA (USER32.@)
3445 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3446 UINT id, UINT flags )
3448 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3449 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3450 id, data );
3451 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3452 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3453 id, data );
3454 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3455 flags & MF_BYPOSITION ? pos : id,
3456 flags & ~MF_REMOVE );
3457 /* Default: MF_INSERT */
3458 return InsertMenuA( hMenu, pos, flags, id, data );
3462 /*******************************************************************
3463 * ChangeMenuW (USER32.@)
3465 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3466 UINT id, UINT flags )
3468 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3469 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3470 id, data );
3471 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3472 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3473 id, data );
3474 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3475 flags & MF_BYPOSITION ? pos : id,
3476 flags & ~MF_REMOVE );
3477 /* Default: MF_INSERT */
3478 return InsertMenuW( hMenu, pos, flags, id, data );
3482 /*******************************************************************
3483 * CheckMenuItem (USER32.@)
3485 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3487 MENUITEM *item;
3488 DWORD ret;
3490 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3491 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3492 ret = item->fState & MF_CHECKED;
3493 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3494 else item->fState &= ~MF_CHECKED;
3495 return ret;
3499 /**********************************************************************
3500 * EnableMenuItem (USER32.@)
3502 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3504 UINT oldflags;
3505 MENUITEM *item;
3506 POPUPMENU *menu;
3508 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3510 /* Get the Popupmenu to access the owner menu */
3511 if (!(menu = MENU_GetMenu(hMenu)))
3512 return (UINT)-1;
3514 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3515 return (UINT)-1;
3517 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3518 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3520 /* If the close item in the system menu change update the close button */
3521 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3523 if (menu->hSysMenuOwner != 0)
3525 RECT rc;
3526 POPUPMENU* parentMenu;
3528 /* Get the parent menu to access*/
3529 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3530 return (UINT)-1;
3532 /* Refresh the frame to reflect the change */
3533 GetWindowRect(parentMenu->hWnd, &rc);
3534 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3535 rc.bottom = 0;
3536 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3540 return oldflags;
3544 /*******************************************************************
3545 * GetMenuStringA (USER32.@)
3547 INT WINAPI GetMenuStringA(
3548 HMENU hMenu, /* [in] menuhandle */
3549 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3550 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3551 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3552 UINT wFlags /* [in] MF_ flags */
3554 MENUITEM *item;
3556 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3557 if (str && nMaxSiz) str[0] = '\0';
3558 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3559 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3560 return 0;
3562 if (!item->text) return 0;
3563 if (!str || !nMaxSiz) return strlenW(item->text);
3564 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3565 str[nMaxSiz-1] = 0;
3566 TRACE("returning '%s'\n", str );
3567 return strlen(str);
3571 /*******************************************************************
3572 * GetMenuStringW (USER32.@)
3574 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3575 LPWSTR str, INT nMaxSiz, UINT wFlags )
3577 MENUITEM *item;
3579 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3580 if (str && nMaxSiz) str[0] = '\0';
3581 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3582 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3583 return 0;
3585 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3586 if( !(item->text)) {
3587 str[0] = 0;
3588 return 0;
3590 lstrcpynW( str, item->text, nMaxSiz );
3591 return strlenW(str);
3595 /**********************************************************************
3596 * HiliteMenuItem (USER32.@)
3598 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3599 UINT wHilite )
3601 LPPOPUPMENU menu;
3602 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3603 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3604 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3605 if (menu->FocusedItem == wItemID) return TRUE;
3606 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3607 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3608 return TRUE;
3612 /**********************************************************************
3613 * GetMenuState (USER32.@)
3615 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3617 MENUITEM *item;
3618 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3619 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3620 debug_print_menuitem (" item: ", item, "");
3621 if (item->fType & MF_POPUP)
3623 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3624 if (!menu) return -1;
3625 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3627 else
3629 /* We used to (from way back then) mask the result to 0xff. */
3630 /* I don't know why and it seems wrong as the documented */
3631 /* return flag MF_SEPARATOR is outside that mask. */
3632 return (item->fType | item->fState);
3637 /**********************************************************************
3638 * GetMenuItemCount (USER32.@)
3640 INT WINAPI GetMenuItemCount( HMENU hMenu )
3642 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3643 if (!menu) return -1;
3644 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3645 return menu->nItems;
3649 /**********************************************************************
3650 * GetMenuItemID (USER32.@)
3652 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3654 MENUITEM * lpmi;
3656 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3657 if (lpmi->fType & MF_POPUP) return -1;
3658 return lpmi->wID;
3663 /*******************************************************************
3664 * InsertMenuW (USER32.@)
3666 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3667 UINT_PTR id, LPCWSTR str )
3669 MENUITEM *item;
3671 if (IS_STRING_ITEM(flags) && str)
3672 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3673 hMenu, pos, flags, id, debugstr_w(str) );
3674 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3675 hMenu, pos, flags, id, str );
3677 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3679 if (!(MENU_SetItemData( item, flags, id, str )))
3681 RemoveMenu( hMenu, pos, flags );
3682 return FALSE;
3685 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3686 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3688 item->hCheckBit = item->hUnCheckBit = 0;
3689 return TRUE;
3693 /*******************************************************************
3694 * InsertMenuA (USER32.@)
3696 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3697 UINT_PTR id, LPCSTR str )
3699 BOOL ret = FALSE;
3701 if (IS_STRING_ITEM(flags) && str)
3703 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3704 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3705 if (newstr)
3707 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3708 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3709 HeapFree( GetProcessHeap(), 0, newstr );
3711 return ret;
3713 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3717 /*******************************************************************
3718 * AppendMenuA (USER32.@)
3720 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3721 UINT_PTR id, LPCSTR data )
3723 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3727 /*******************************************************************
3728 * AppendMenuW (USER32.@)
3730 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3731 UINT_PTR id, LPCWSTR data )
3733 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3737 /**********************************************************************
3738 * RemoveMenu (USER32.@)
3740 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3742 LPPOPUPMENU menu;
3743 MENUITEM *item;
3745 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3746 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3747 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3749 /* Remove item */
3751 MENU_FreeItemData( item );
3753 if (--menu->nItems == 0)
3755 HeapFree( GetProcessHeap(), 0, menu->items );
3756 menu->items = NULL;
3758 else
3760 while(nPos < menu->nItems)
3762 *item = *(item+1);
3763 item++;
3764 nPos++;
3766 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3767 menu->nItems * sizeof(MENUITEM) );
3769 return TRUE;
3773 /**********************************************************************
3774 * DeleteMenu (USER32.@)
3776 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3778 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3779 if (!item) return FALSE;
3780 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3781 /* nPos is now the position of the item */
3782 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3783 return TRUE;
3787 /*******************************************************************
3788 * ModifyMenuW (USER32.@)
3790 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3791 UINT_PTR id, LPCWSTR str )
3793 MENUITEM *item;
3795 if (IS_STRING_ITEM(flags))
3796 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3797 else
3798 TRACE("%p %d %04x %04x %p\n", hMenu, pos, flags, id, str );
3800 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3801 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3802 return MENU_SetItemData( item, flags, id, str );
3806 /*******************************************************************
3807 * ModifyMenuA (USER32.@)
3809 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3810 UINT_PTR id, LPCSTR str )
3812 BOOL ret = FALSE;
3814 if (IS_STRING_ITEM(flags) && str)
3816 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3817 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3818 if (newstr)
3820 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3821 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3822 HeapFree( GetProcessHeap(), 0, newstr );
3824 return ret;
3826 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3830 /**********************************************************************
3831 * CreatePopupMenu (USER32.@)
3833 HMENU WINAPI CreatePopupMenu(void)
3835 HMENU hmenu;
3836 POPUPMENU *menu;
3838 if (!(hmenu = CreateMenu())) return 0;
3839 menu = MENU_GetMenu( hmenu );
3840 menu->wFlags |= MF_POPUP;
3841 menu->bTimeToHide = FALSE;
3842 return hmenu;
3846 /**********************************************************************
3847 * GetMenuCheckMarkDimensions (USER.417)
3848 * GetMenuCheckMarkDimensions (USER32.@)
3850 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3852 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3856 /**********************************************************************
3857 * SetMenuItemBitmaps (USER32.@)
3859 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3860 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3862 MENUITEM *item;
3863 TRACE("(%p, %04x, %04x, %p, %p)\n",
3864 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3865 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3867 if (!hNewCheck && !hNewUnCheck)
3869 item->fState &= ~MF_USECHECKBITMAPS;
3871 else /* Install new bitmaps */
3873 item->hCheckBit = hNewCheck;
3874 item->hUnCheckBit = hNewUnCheck;
3875 item->fState |= MF_USECHECKBITMAPS;
3877 return TRUE;
3881 /**********************************************************************
3882 * CreateMenu (USER32.@)
3884 HMENU WINAPI CreateMenu(void)
3886 HMENU hMenu;
3887 LPPOPUPMENU menu;
3888 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3889 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3891 ZeroMemory(menu, sizeof(POPUPMENU));
3892 menu->wMagic = MENU_MAGIC;
3893 menu->FocusedItem = NO_SELECTED_ITEM;
3894 menu->bTimeToHide = FALSE;
3896 TRACE("return %p\n", hMenu );
3898 return hMenu;
3902 /**********************************************************************
3903 * DestroyMenu (USER32.@)
3905 BOOL WINAPI DestroyMenu( HMENU hMenu )
3907 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3909 TRACE("(%p)\n", hMenu);
3912 if (!lppop) return FALSE;
3914 lppop->wMagic = 0; /* Mark it as destroyed */
3916 /* DestroyMenu should not destroy system menu popup owner */
3917 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3919 DestroyWindow( lppop->hWnd );
3920 lppop->hWnd = 0;
3923 if (lppop->items) /* recursively destroy submenus */
3925 int i;
3926 MENUITEM *item = lppop->items;
3927 for (i = lppop->nItems; i > 0; i--, item++)
3929 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3930 MENU_FreeItemData( item );
3932 HeapFree( GetProcessHeap(), 0, lppop->items );
3934 USER_HEAP_FREE( hMenu );
3935 return TRUE;
3939 /**********************************************************************
3940 * GetSystemMenu (USER32.@)
3942 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3944 WND *wndPtr = WIN_GetPtr( hWnd );
3945 HMENU retvalue = 0;
3947 if (wndPtr == WND_DESKTOP) return 0;
3948 if (wndPtr == WND_OTHER_PROCESS)
3950 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3952 else if (wndPtr)
3954 if (wndPtr->hSysMenu && bRevert)
3956 DestroyMenu(wndPtr->hSysMenu);
3957 wndPtr->hSysMenu = 0;
3960 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3961 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
3963 if( wndPtr->hSysMenu )
3965 POPUPMENU *menu;
3966 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3968 /* Store the dummy sysmenu handle to facilitate the refresh */
3969 /* of the close button if the SC_CLOSE item change */
3970 menu = MENU_GetMenu(retvalue);
3971 if ( menu )
3972 menu->hSysMenuOwner = wndPtr->hSysMenu;
3974 WIN_ReleasePtr( wndPtr );
3976 return bRevert ? 0 : retvalue;
3980 /*******************************************************************
3981 * SetSystemMenu (USER32.@)
3983 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3985 WND *wndPtr = WIN_GetPtr( hwnd );
3987 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3989 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3990 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3991 WIN_ReleasePtr( wndPtr );
3992 return TRUE;
3994 return FALSE;
3998 /**********************************************************************
3999 * GetMenu (USER32.@)
4001 HMENU WINAPI GetMenu( HWND hWnd )
4003 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4004 TRACE("for %p returning %p\n", hWnd, retvalue);
4005 return retvalue;
4008 /**********************************************************************
4009 * GetMenuBarInfo (USER32.@)
4011 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4013 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
4014 return FALSE;
4017 /**********************************************************************
4018 * MENU_SetMenu
4020 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4021 * SetWindowPos call that would result if SetMenu were called directly.
4023 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4025 TRACE("(%p, %p);\n", hWnd, hMenu);
4027 if (hMenu && !IsMenu(hMenu))
4029 WARN("hMenu %p is not a menu handle\n", hMenu);
4030 SetLastError(ERROR_INVALID_MENU_HANDLE);
4031 return FALSE;
4033 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4034 return FALSE;
4036 hWnd = WIN_GetFullHandle( hWnd );
4037 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4039 if (hMenu != 0)
4041 LPPOPUPMENU lpmenu;
4043 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4045 lpmenu->hWnd = hWnd;
4046 lpmenu->Height = 0; /* Make sure we recalculate the size */
4048 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4049 return TRUE;
4053 /**********************************************************************
4054 * SetMenu (USER32.@)
4056 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4058 if(!MENU_SetMenu(hWnd, hMenu))
4059 return FALSE;
4061 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4062 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4063 return TRUE;
4067 /**********************************************************************
4068 * GetSubMenu (USER32.@)
4070 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4072 MENUITEM * lpmi;
4074 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4075 if (!(lpmi->fType & MF_POPUP)) return 0;
4076 return lpmi->hSubMenu;
4080 /**********************************************************************
4081 * DrawMenuBar (USER32.@)
4083 BOOL WINAPI DrawMenuBar( HWND hWnd )
4085 LPPOPUPMENU lppop;
4086 HMENU hMenu = GetMenu(hWnd);
4088 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4089 return FALSE;
4090 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4092 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4093 lppop->hwndOwner = hWnd;
4094 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4095 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4096 return TRUE;
4099 /***********************************************************************
4100 * DrawMenuBarTemp (USER32.@)
4102 * UNDOCUMENTED !!
4104 * called by W98SE desk.cpl Control Panel Applet
4106 * Not 100% sure about the param names, but close.
4108 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4110 LPPOPUPMENU lppop;
4111 UINT i,retvalue;
4112 HFONT hfontOld = 0;
4113 BOOL flat_menu = FALSE;
4115 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4117 if (!hMenu)
4118 hMenu = GetMenu(hwnd);
4120 if (!hFont)
4121 hFont = get_menu_font(FALSE);
4123 lppop = MENU_GetMenu( hMenu );
4124 if (lppop == NULL || lprect == NULL)
4126 retvalue = GetSystemMetrics(SM_CYMENU);
4127 goto END;
4130 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4132 hfontOld = SelectObject( hDC, hFont);
4134 if (lppop->Height == 0)
4135 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4137 lprect->bottom = lprect->top + lppop->Height;
4139 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4141 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4142 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4143 LineTo( hDC, lprect->right, lprect->bottom );
4145 if (lppop->nItems == 0)
4147 retvalue = GetSystemMetrics(SM_CYMENU);
4148 goto END;
4151 for (i = 0; i < lppop->nItems; i++)
4153 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4154 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4156 retvalue = lppop->Height;
4158 END:
4159 if (hfontOld) SelectObject (hDC, hfontOld);
4160 return retvalue;
4163 /***********************************************************************
4164 * EndMenu (USER.187)
4165 * EndMenu (USER32.@)
4167 void WINAPI EndMenu(void)
4169 /* if we are in the menu code, and it is active */
4170 if (!fEndMenu && top_popup)
4172 /* terminate the menu handling code */
4173 fEndMenu = TRUE;
4175 /* needs to be posted to wakeup the internal menu handler */
4176 /* which will now terminate the menu, in the event that */
4177 /* the main window was minimized, or lost focus, so we */
4178 /* don't end up with an orphaned menu */
4179 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4184 /***********************************************************************
4185 * LookupMenuHandle (USER.217)
4187 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4189 HMENU hmenu32 = HMENU_32(hmenu);
4190 UINT id32 = id;
4191 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4192 else return HMENU_16(hmenu32);
4196 /**********************************************************************
4197 * LoadMenu (USER.150)
4199 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4201 HRSRC16 hRsrc;
4202 HGLOBAL16 handle;
4203 HMENU16 hMenu;
4205 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4206 if (!name) return 0;
4208 instance = GetExePtr( instance );
4209 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4210 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4211 hMenu = LoadMenuIndirect16(LockResource16(handle));
4212 FreeResource16( handle );
4213 return hMenu;
4217 /*****************************************************************
4218 * LoadMenuA (USER32.@)
4220 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4222 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4223 if (!hrsrc) return 0;
4224 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4228 /*****************************************************************
4229 * LoadMenuW (USER32.@)
4231 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4233 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4234 if (!hrsrc) return 0;
4235 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4239 /**********************************************************************
4240 * LoadMenuIndirect (USER.220)
4242 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4244 HMENU hMenu;
4245 WORD version, offset;
4246 LPCSTR p = (LPCSTR)template;
4248 TRACE("(%p)\n", template );
4249 version = GET_WORD(p);
4250 p += sizeof(WORD);
4251 if (version)
4253 WARN("version must be 0 for Win16\n" );
4254 return 0;
4256 offset = GET_WORD(p);
4257 p += sizeof(WORD) + offset;
4258 if (!(hMenu = CreateMenu())) return 0;
4259 if (!MENU_ParseResource( p, hMenu, FALSE ))
4261 DestroyMenu( hMenu );
4262 return 0;
4264 return HMENU_16(hMenu);
4268 /**********************************************************************
4269 * LoadMenuIndirectW (USER32.@)
4271 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4273 HMENU hMenu;
4274 WORD version, offset;
4275 LPCSTR p = (LPCSTR)template;
4277 version = GET_WORD(p);
4278 p += sizeof(WORD);
4279 TRACE("%p, ver %d\n", template, version );
4280 switch (version)
4282 case 0: /* standard format is version of 0 */
4283 offset = GET_WORD(p);
4284 p += sizeof(WORD) + offset;
4285 if (!(hMenu = CreateMenu())) return 0;
4286 if (!MENU_ParseResource( p, hMenu, TRUE ))
4288 DestroyMenu( hMenu );
4289 return 0;
4291 return hMenu;
4292 case 1: /* extended format is version of 1 */
4293 offset = GET_WORD(p);
4294 p += sizeof(WORD) + offset;
4295 if (!(hMenu = CreateMenu())) return 0;
4296 if (!MENUEX_ParseResource( p, hMenu))
4298 DestroyMenu( hMenu );
4299 return 0;
4301 return hMenu;
4302 default:
4303 ERR("version %d not supported.\n", version);
4304 return 0;
4309 /**********************************************************************
4310 * LoadMenuIndirectA (USER32.@)
4312 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4314 return LoadMenuIndirectW( template );
4318 /**********************************************************************
4319 * IsMenu (USER32.@)
4321 BOOL WINAPI IsMenu(HMENU hmenu)
4323 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4324 return menu != NULL;
4327 /**********************************************************************
4328 * GetMenuItemInfo_common
4331 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4332 LPMENUITEMINFOW lpmii, BOOL unicode)
4334 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4336 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4338 if (!menu)
4339 return FALSE;
4341 if( lpmii->fMask & MIIM_TYPE) {
4342 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4343 WARN("invalid combination of fMask bits used\n");
4344 /* this does not happen on Win9x/ME */
4345 SetLastError( ERROR_INVALID_PARAMETER);
4346 return FALSE;
4348 lpmii->fType = menu->fType & ~MF_POPUP;
4349 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4350 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4351 if( lpmii->fType & MFT_BITMAP) {
4352 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4353 lpmii->cch = 0;
4354 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4355 /* this does not happen on Win9x/ME */
4356 lpmii->dwTypeData = 0;
4357 lpmii->cch = 0;
4361 /* copy the text string */
4362 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4363 if( !menu->text ) {
4364 if(lpmii->dwTypeData && lpmii->cch) {
4365 lpmii->cch = 0;
4366 if( unicode)
4367 *((WCHAR *)lpmii->dwTypeData) = 0;
4368 else
4369 *((CHAR *)lpmii->dwTypeData) = 0;
4371 } else {
4372 int len;
4373 if (unicode)
4375 len = strlenW(menu->text);
4376 if(lpmii->dwTypeData && lpmii->cch)
4377 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4379 else
4381 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4382 0, NULL, NULL ) - 1;
4383 if(lpmii->dwTypeData && lpmii->cch)
4384 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4385 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4386 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4388 /* if we've copied a substring we return its length */
4389 if(lpmii->dwTypeData && lpmii->cch)
4390 if (lpmii->cch <= len + 1)
4391 lpmii->cch--;
4392 else
4393 lpmii->cch = len;
4394 else {
4395 /* return length of string */
4396 /* not on Win9x/ME if fType & MFT_BITMAP */
4397 lpmii->cch = len;
4402 if (lpmii->fMask & MIIM_FTYPE)
4403 lpmii->fType = menu->fType & ~MF_POPUP;
4405 if (lpmii->fMask & MIIM_BITMAP)
4406 lpmii->hbmpItem = menu->hbmpItem;
4408 if (lpmii->fMask & MIIM_STATE)
4409 lpmii->fState = menu->fState;
4411 if (lpmii->fMask & MIIM_ID)
4412 lpmii->wID = menu->wID;
4414 if (lpmii->fMask & MIIM_SUBMENU)
4415 lpmii->hSubMenu = menu->hSubMenu;
4416 else {
4417 /* hSubMenu is always cleared
4418 * (not on Win9x/ME ) */
4419 lpmii->hSubMenu = 0;
4422 if (lpmii->fMask & MIIM_CHECKMARKS) {
4423 lpmii->hbmpChecked = menu->hCheckBit;
4424 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4426 if (lpmii->fMask & MIIM_DATA)
4427 lpmii->dwItemData = menu->dwItemData;
4429 return TRUE;
4432 /**********************************************************************
4433 * GetMenuItemInfoA (USER32.@)
4435 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4436 LPMENUITEMINFOA lpmii)
4438 BOOL ret;
4439 MENUITEMINFOA mii;
4440 if( lpmii->cbSize != sizeof( mii) &&
4441 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4442 SetLastError( ERROR_INVALID_PARAMETER);
4443 return FALSE;
4445 memcpy( &mii, lpmii, lpmii->cbSize);
4446 mii.cbSize = sizeof( mii);
4447 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4448 (LPMENUITEMINFOW)&mii, FALSE);
4449 mii.cbSize = lpmii->cbSize;
4450 memcpy( lpmii, &mii, mii.cbSize);
4451 return ret;
4454 /**********************************************************************
4455 * GetMenuItemInfoW (USER32.@)
4457 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4458 LPMENUITEMINFOW lpmii)
4460 BOOL ret;
4461 MENUITEMINFOW mii;
4462 if( lpmii->cbSize != sizeof( mii) &&
4463 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4464 SetLastError( ERROR_INVALID_PARAMETER);
4465 return FALSE;
4467 memcpy( &mii, lpmii, lpmii->cbSize);
4468 mii.cbSize = sizeof( mii);
4469 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4470 mii.cbSize = lpmii->cbSize;
4471 memcpy( lpmii, &mii, mii.cbSize);
4472 return ret;
4476 /* set a menu item text from a ASCII or Unicode string */
4477 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4479 if (!text)
4480 menu->text = NULL;
4481 else if (unicode)
4483 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4484 strcpyW( menu->text, text );
4486 else
4488 LPCSTR str = (LPCSTR)text;
4489 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4490 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4491 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4496 /**********************************************************************
4497 * SetMenuItemInfo_common
4500 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4501 const MENUITEMINFOW *lpmii,
4502 BOOL unicode)
4504 if (!menu) return FALSE;
4506 debug_print_menuitem("SetmenuItemInfo_common from: ", menu, "");
4508 if (lpmii->fMask & MIIM_TYPE ) {
4509 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4510 WARN("invalid combination of fMask bits used\n");
4511 /* this does not happen on Win9x/ME */
4512 SetLastError( ERROR_INVALID_PARAMETER);
4513 return FALSE;
4515 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4516 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4517 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4519 if (IS_STRING_ITEM(menu->fType)) {
4520 HeapFree(GetProcessHeap(), 0, menu->text);
4521 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4522 } else if( (menu->fType) & MFT_BITMAP)
4523 menu->hbmpItem = (HBITMAP)lpmii->dwTypeData;
4526 if (lpmii->fMask & MIIM_FTYPE ) {
4527 if(( lpmii->fType & MFT_BITMAP)) {
4528 SetLastError( ERROR_INVALID_PARAMETER);
4529 return FALSE;
4531 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4532 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4534 if (lpmii->fMask & MIIM_STRING ) {
4535 /* free the string when used */
4536 HeapFree(GetProcessHeap(), 0, menu->text);
4537 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4540 if (lpmii->fMask & MIIM_STATE)
4542 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4543 menu->fState = lpmii->fState;
4546 if (lpmii->fMask & MIIM_ID)
4547 menu->wID = lpmii->wID;
4549 if (lpmii->fMask & MIIM_SUBMENU) {
4550 menu->hSubMenu = lpmii->hSubMenu;
4551 if (menu->hSubMenu) {
4552 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4553 if (subMenu) {
4554 subMenu->wFlags |= MF_POPUP;
4555 menu->fType |= MF_POPUP;
4557 else {
4558 SetLastError( ERROR_INVALID_PARAMETER);
4559 return FALSE;
4562 else
4563 menu->fType &= ~MF_POPUP;
4566 if (lpmii->fMask & MIIM_CHECKMARKS)
4568 if (lpmii->fType & MFT_RADIOCHECK)
4569 menu->fType |= MFT_RADIOCHECK;
4571 menu->hCheckBit = lpmii->hbmpChecked;
4572 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4574 if (lpmii->fMask & MIIM_DATA)
4575 menu->dwItemData = lpmii->dwItemData;
4577 if (lpmii->fMask & MIIM_BITMAP)
4578 menu->hbmpItem = lpmii->hbmpItem;
4580 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4581 menu->fType |= MFT_SEPARATOR;
4583 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4584 return TRUE;
4587 /**********************************************************************
4588 * SetMenuItemInfoA (USER32.@)
4590 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4591 const MENUITEMINFOA *lpmii)
4593 MENUITEMINFOA mii;
4594 if( lpmii->cbSize != sizeof( mii) &&
4595 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4596 SetLastError( ERROR_INVALID_PARAMETER);
4597 return FALSE;
4599 memcpy( &mii, lpmii, lpmii->cbSize);
4600 if( lpmii->cbSize != sizeof( mii)) {
4601 mii.cbSize = sizeof( mii);
4602 mii.hbmpItem = NULL;
4604 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4605 (const MENUITEMINFOW *)&mii, FALSE);
4608 /**********************************************************************
4609 * SetMenuItemInfoW (USER32.@)
4611 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4612 const MENUITEMINFOW *lpmii)
4614 MENUITEMINFOW mii;
4615 if( lpmii->cbSize != sizeof( mii) &&
4616 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4617 SetLastError( ERROR_INVALID_PARAMETER);
4618 return FALSE;
4620 memcpy( &mii, lpmii, lpmii->cbSize);
4621 if( lpmii->cbSize != sizeof( mii)) {
4622 mii.cbSize = sizeof( mii);
4623 mii.hbmpItem = NULL;
4625 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4626 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4629 /**********************************************************************
4630 * SetMenuDefaultItem (USER32.@)
4633 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4635 UINT i;
4636 POPUPMENU *menu;
4637 MENUITEM *item;
4639 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4641 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4643 /* reset all default-item flags */
4644 item = menu->items;
4645 for (i = 0; i < menu->nItems; i++, item++)
4647 item->fState &= ~MFS_DEFAULT;
4650 /* no default item */
4651 if ( -1 == uItem)
4653 return TRUE;
4656 item = menu->items;
4657 if ( bypos )
4659 if ( uItem >= menu->nItems ) return FALSE;
4660 item[uItem].fState |= MFS_DEFAULT;
4661 return TRUE;
4663 else
4665 for (i = 0; i < menu->nItems; i++, item++)
4667 if (item->wID == uItem)
4669 item->fState |= MFS_DEFAULT;
4670 return TRUE;
4675 return FALSE;
4678 /**********************************************************************
4679 * GetMenuDefaultItem (USER32.@)
4681 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4683 POPUPMENU *menu;
4684 MENUITEM * item;
4685 UINT i = 0;
4687 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4689 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4691 /* find default item */
4692 item = menu->items;
4694 /* empty menu */
4695 if (! item) return -1;
4697 while ( !( item->fState & MFS_DEFAULT ) )
4699 i++; item++;
4700 if (i >= menu->nItems ) return -1;
4703 /* default: don't return disabled items */
4704 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4706 /* search rekursiv when needed */
4707 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4709 UINT ret;
4710 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4711 if ( -1 != ret ) return ret;
4713 /* when item not found in submenu, return the popup item */
4715 return ( bypos ) ? i : item->wID;
4720 /**********************************************************************
4721 * InsertMenuItemA (USER32.@)
4723 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4724 const MENUITEMINFOA *lpmii)
4726 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4727 MENUITEMINFOA mii;
4728 if( lpmii->cbSize != sizeof( mii) &&
4729 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4730 SetLastError( ERROR_INVALID_PARAMETER);
4731 return FALSE;
4733 memcpy( &mii, lpmii, lpmii->cbSize);
4734 if( lpmii->cbSize != sizeof( mii)) {
4735 mii.cbSize = sizeof( mii);
4736 mii.hbmpItem = NULL;
4738 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4742 /**********************************************************************
4743 * InsertMenuItemW (USER32.@)
4745 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4746 const MENUITEMINFOW *lpmii)
4748 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4749 MENUITEMINFOW mii;
4750 if( lpmii->cbSize != sizeof( mii) &&
4751 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4752 SetLastError( ERROR_INVALID_PARAMETER);
4753 return FALSE;
4755 memcpy( &mii, lpmii, lpmii->cbSize);
4756 if( lpmii->cbSize != sizeof( mii)) {
4757 mii.cbSize = sizeof( mii);
4758 mii.hbmpItem = NULL;
4760 return SetMenuItemInfo_common(item, &mii, TRUE);
4763 /**********************************************************************
4764 * CheckMenuRadioItem (USER32.@)
4767 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4768 UINT first, UINT last, UINT check,
4769 UINT bypos)
4771 MENUITEM *mifirst, *milast, *micheck;
4772 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4774 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4776 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4777 milast = MENU_FindItem (&mlast, &last, bypos);
4778 micheck = MENU_FindItem (&mcheck, &check, bypos);
4780 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4781 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4782 micheck > milast || micheck < mifirst)
4783 return FALSE;
4785 while (mifirst <= milast)
4787 if (mifirst == micheck)
4789 mifirst->fType |= MFT_RADIOCHECK;
4790 mifirst->fState |= MFS_CHECKED;
4791 } else {
4792 mifirst->fType &= ~MFT_RADIOCHECK;
4793 mifirst->fState &= ~MFS_CHECKED;
4795 mifirst++;
4798 return TRUE;
4802 /**********************************************************************
4803 * GetMenuItemRect (USER32.@)
4805 * ATTENTION: Here, the returned values in rect are the screen
4806 * coordinates of the item just like if the menu was
4807 * always on the upper left side of the application.
4810 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4811 LPRECT rect)
4813 POPUPMENU *itemMenu;
4814 MENUITEM *item;
4815 HWND referenceHwnd;
4817 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4819 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4820 referenceHwnd = hwnd;
4822 if(!hwnd)
4824 itemMenu = MENU_GetMenu(hMenu);
4825 if (itemMenu == NULL)
4826 return FALSE;
4828 if(itemMenu->hWnd == 0)
4829 return FALSE;
4830 referenceHwnd = itemMenu->hWnd;
4833 if ((rect == NULL) || (item == NULL))
4834 return FALSE;
4836 *rect = item->rect;
4838 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4840 return TRUE;
4844 /**********************************************************************
4845 * SetMenuInfo (USER32.@)
4847 * FIXME
4848 * MIM_APPLYTOSUBMENUS
4849 * actually use the items to draw the menu
4851 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4853 POPUPMENU *menu;
4855 TRACE("(%p %p)\n", hMenu, lpmi);
4857 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4860 if (lpmi->fMask & MIM_BACKGROUND)
4861 menu->hbrBack = lpmi->hbrBack;
4863 if (lpmi->fMask & MIM_HELPID)
4864 menu->dwContextHelpID = lpmi->dwContextHelpID;
4866 if (lpmi->fMask & MIM_MAXHEIGHT)
4867 menu->cyMax = lpmi->cyMax;
4869 if (lpmi->fMask & MIM_MENUDATA)
4870 menu->dwMenuData = lpmi->dwMenuData;
4872 if (lpmi->fMask & MIM_STYLE)
4874 menu->dwStyle = lpmi->dwStyle;
4875 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4876 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4877 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4878 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4881 return TRUE;
4883 return FALSE;
4886 /**********************************************************************
4887 * GetMenuInfo (USER32.@)
4889 * NOTES
4890 * win98/NT5.0
4893 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4894 { POPUPMENU *menu;
4896 TRACE("(%p %p)\n", hMenu, lpmi);
4898 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4901 if (lpmi->fMask & MIM_BACKGROUND)
4902 lpmi->hbrBack = menu->hbrBack;
4904 if (lpmi->fMask & MIM_HELPID)
4905 lpmi->dwContextHelpID = menu->dwContextHelpID;
4907 if (lpmi->fMask & MIM_MAXHEIGHT)
4908 lpmi->cyMax = menu->cyMax;
4910 if (lpmi->fMask & MIM_MENUDATA)
4911 lpmi->dwMenuData = menu->dwMenuData;
4913 if (lpmi->fMask & MIM_STYLE)
4914 lpmi->dwStyle = menu->dwStyle;
4916 return TRUE;
4918 return FALSE;
4922 /**********************************************************************
4923 * SetMenuContextHelpId (USER32.@)
4925 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4927 LPPOPUPMENU menu;
4929 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4931 if ((menu = MENU_GetMenu(hMenu)))
4933 menu->dwContextHelpID = dwContextHelpID;
4934 return TRUE;
4936 return FALSE;
4940 /**********************************************************************
4941 * GetMenuContextHelpId (USER32.@)
4943 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4945 LPPOPUPMENU menu;
4947 TRACE("(%p)\n", hMenu);
4949 if ((menu = MENU_GetMenu(hMenu)))
4951 return menu->dwContextHelpID;
4953 return 0;
4956 /**********************************************************************
4957 * MenuItemFromPoint (USER32.@)
4959 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4961 POPUPMENU *menu = MENU_GetMenu(hMenu);
4962 UINT pos;
4964 /*FIXME: Do we have to handle hWnd here? */
4965 if (!menu) return -1;
4966 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4967 return pos;
4971 /**********************************************************************
4972 * translate_accelerator
4974 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4975 BYTE fVirt, WORD key, WORD cmd )
4977 INT mask = 0;
4978 UINT mesg = 0;
4980 if (wParam != key) return FALSE;
4982 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4983 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4984 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4986 if (message == WM_CHAR || message == WM_SYSCHAR)
4988 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4990 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4991 goto found;
4994 else
4996 if(fVirt & FVIRTKEY)
4998 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4999 wParam, 0xff & HIWORD(lParam));
5001 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5002 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5004 else
5006 if (!(lParam & 0x01000000)) /* no special_key */
5008 if ((fVirt & FALT) && (lParam & 0x20000000))
5009 { /* ^^ ALT pressed */
5010 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5011 goto found;
5016 return FALSE;
5018 found:
5019 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5020 mesg = 1;
5021 else
5023 HMENU hMenu, hSubMenu, hSysMenu;
5024 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5026 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5027 hSysMenu = get_win_sys_menu( hWnd );
5029 /* find menu item and ask application to initialize it */
5030 /* 1. in the system menu */
5031 hSubMenu = hSysMenu;
5032 nPos = cmd;
5033 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5035 if (GetCapture())
5036 mesg = 2;
5037 if (!IsWindowEnabled(hWnd))
5038 mesg = 3;
5039 else
5041 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5042 if(hSubMenu != hSysMenu)
5044 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5045 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5046 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5048 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5051 else /* 2. in the window's menu */
5053 hSubMenu = hMenu;
5054 nPos = cmd;
5055 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5057 if (GetCapture())
5058 mesg = 2;
5059 if (!IsWindowEnabled(hWnd))
5060 mesg = 3;
5061 else
5063 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5064 if(hSubMenu != hMenu)
5066 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5067 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5068 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5070 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5075 if (mesg == 0)
5077 if (uSysStat != (UINT)-1)
5079 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5080 mesg=4;
5081 else
5082 mesg=WM_SYSCOMMAND;
5084 else
5086 if (uStat != (UINT)-1)
5088 if (IsIconic(hWnd))
5089 mesg=5;
5090 else
5092 if (uStat & (MF_DISABLED|MF_GRAYED))
5093 mesg=6;
5094 else
5095 mesg=WM_COMMAND;
5098 else
5099 mesg=WM_COMMAND;
5104 if( mesg==WM_COMMAND )
5106 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5107 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5109 else if( mesg==WM_SYSCOMMAND )
5111 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5112 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5114 else
5116 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5117 * #0: unknown (please report!)
5118 * #1: for WM_KEYUP,WM_SYSKEYUP
5119 * #2: mouse is captured
5120 * #3: window is disabled
5121 * #4: it's a disabled system menu option
5122 * #5: it's a menu option, but window is iconic
5123 * #6: it's a menu option, but disabled
5125 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5126 if(mesg==0)
5127 ERR_(accel)(" unknown reason - please report!\n");
5129 return TRUE;
5132 /**********************************************************************
5133 * TranslateAcceleratorA (USER32.@)
5134 * TranslateAccelerator (USER32.@)
5136 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5138 /* YES, Accel16! */
5139 LPACCEL16 lpAccelTbl;
5140 int i;
5141 WPARAM wParam;
5143 if (!hWnd || !msg) return 0;
5145 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5147 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5148 return 0;
5151 wParam = msg->wParam;
5153 switch (msg->message)
5155 case WM_KEYDOWN:
5156 case WM_SYSKEYDOWN:
5157 break;
5159 case WM_CHAR:
5160 case WM_SYSCHAR:
5162 char ch = LOWORD(wParam);
5163 WCHAR wch;
5164 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5165 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5167 break;
5169 default:
5170 return 0;
5173 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5174 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5175 i = 0;
5178 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5179 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5180 return 1;
5181 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5183 return 0;
5186 /**********************************************************************
5187 * TranslateAcceleratorW (USER32.@)
5189 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5191 /* YES, Accel16! */
5192 LPACCEL16 lpAccelTbl;
5193 int i;
5195 if (!hWnd || !msg) return 0;
5197 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5199 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5200 return 0;
5203 switch (msg->message)
5205 case WM_KEYDOWN:
5206 case WM_SYSKEYDOWN:
5207 case WM_CHAR:
5208 case WM_SYSCHAR:
5209 break;
5211 default:
5212 return 0;
5215 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5216 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5217 i = 0;
5220 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5221 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5222 return 1;
5223 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5225 return 0;