Switch focus to activated window even if no window previously had the
[wine/wine64.git] / controls / menu.c
blob931d438e3499bbfe5c269d36cc185d21dc0a8ce6
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 */
9 /*
10 * Note: the style MF_MOUSESELECT is used to mark popup items that
11 * have been selected, i.e. their popup menu is currently displayed.
12 * This is probably not the meaning this style has in MS-Windows.
15 #include "wine/port.h"
17 #include <assert.h>
18 #include <ctype.h>
19 #include <stdlib.h>
20 #include <string.h>
22 #include "windef.h"
23 #include "winnls.h"
24 #include "wingdi.h"
25 #include "wine/winbase16.h"
26 #include "wine/winuser16.h"
27 #include "wine/unicode.h"
28 #include "win.h"
29 #include "controls.h"
30 #include "nonclient.h"
31 #include "user.h"
32 #include "message.h"
34 #include "debugtools.h"
36 DEFAULT_DEBUG_CHANNEL(menu);
37 DECLARE_DEBUG_CHANNEL(accel);
39 /* internal popup menu window messages */
41 #define MM_SETMENUHANDLE (WM_USER + 0)
42 #define MM_GETMENUHANDLE (WM_USER + 1)
44 /* Menu item structure */
45 typedef struct {
46 /* ----------- MENUITEMINFO Stuff ----------- */
47 UINT fType; /* Item type. */
48 UINT fState; /* Item state. */
49 UINT wID; /* Item id. */
50 HMENU hSubMenu; /* Pop-up menu. */
51 HBITMAP hCheckBit; /* Bitmap when checked. */
52 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
53 LPWSTR text; /* Item text or bitmap handle. */
54 DWORD dwItemData; /* Application defined. */
55 DWORD dwTypeData; /* depends on fMask */
56 HBITMAP hbmpItem; /* bitmap in win98 style menus */
57 /* ----------- Wine stuff ----------- */
58 RECT rect; /* Item area (relative to menu window) */
59 UINT xTab; /* X position of text after Tab */
60 } MENUITEM;
62 /* Popup menu structure */
63 typedef struct {
64 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
65 WORD wMagic; /* Magic number */
66 WORD Width; /* Width of the whole menu */
67 WORD Height; /* Height of the whole menu */
68 UINT nItems; /* Number of items in the menu */
69 HWND hWnd; /* Window containing the menu */
70 MENUITEM *items; /* Array of menu items */
71 UINT FocusedItem; /* Currently focused item */
72 HWND hwndOwner; /* window receiving the messages for ownerdraw */
73 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
74 /* ------------ MENUINFO members ------ */
75 DWORD dwStyle; /* Extended mennu style */
76 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
77 HBRUSH hbrBack; /* brush for menu background */
78 DWORD dwContextHelpID;
79 DWORD dwMenuData; /* application defined value */
80 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
81 } POPUPMENU, *LPPOPUPMENU;
83 /* internal flags for menu tracking */
85 #define TF_ENDMENU 0x0001
86 #define TF_SUSPENDPOPUP 0x0002
87 #define TF_SKIPREMOVE 0x0004
89 typedef struct
91 UINT trackFlags;
92 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
93 HMENU hTopMenu; /* initial menu */
94 HWND hOwnerWnd; /* where notifications are sent */
95 POINT pt;
96 } MTRACKER;
98 #define MENU_MAGIC 0x554d /* 'MU' */
100 #define ITEM_PREV -1
101 #define ITEM_NEXT 1
103 /* Internal MENU_TrackMenu() flags */
104 #define TPM_INTERNAL 0xF0000000
105 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
106 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
107 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
109 /* popup menu shade thickness */
110 #define POPUP_XSHADE 4
111 #define POPUP_YSHADE 4
113 /* Space between 2 menu bar items */
114 #define MENU_BAR_ITEMS_SPACE 12
116 /* Minimum width of a tab character */
117 #define MENU_TAB_SPACE 8
119 /* Height of a separator item */
120 #define SEPARATOR_HEIGHT 5
122 /* (other menu->FocusedItem values give the position of the focused item) */
123 #define NO_SELECTED_ITEM 0xffff
125 #define MENU_ITEM_TYPE(flags) \
126 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
128 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
129 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
130 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
132 #define IS_SYSTEM_MENU(menu) \
133 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
135 #define IS_SYSTEM_POPUP(menu) \
136 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
138 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
139 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
140 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
141 MF_POPUP | MF_SYSMENU | MF_HELP)
142 #define STATE_MASK (~TYPE_MASK)
144 /* Dimension of the menu bitmaps */
145 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
147 static HBITMAP hStdMnArrow = 0;
149 /* Minimze/restore/close buttons to be inserted in menubar */
150 static HBITMAP hBmpMinimize = 0;
151 static HBITMAP hBmpMinimizeD = 0;
152 static HBITMAP hBmpMaximize = 0;
153 static HBITMAP hBmpMaximizeD = 0;
154 static HBITMAP hBmpClose = 0;
155 static HBITMAP hBmpCloseD = 0;
158 static HBRUSH hShadeBrush = 0;
159 static HFONT hMenuFont = 0;
160 static HFONT hMenuFontBold = 0;
162 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
164 /* Use global popup window because there's no way 2 menus can
165 * be tracked at the same time. */
166 static HWND top_popup;
168 /* Flag set by EndMenu() to force an exit from menu tracking */
169 static BOOL fEndMenu = FALSE;
171 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
174 /*********************************************************************
175 * menu class descriptor
177 const struct builtin_class_descr MENU_builtin_class =
179 POPUPMENU_CLASS_ATOM, /* name */
180 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
181 NULL, /* procA (winproc is Unicode only) */
182 PopupMenuWndProc, /* procW */
183 sizeof(HMENU), /* extra */
184 IDC_ARROWA, /* cursor */
185 COLOR_MENU+1 /* brush */
189 /***********************************************************************
190 * debug_print_menuitem
192 * Print a menuitem in readable form.
195 #define debug_print_menuitem(pre, mp, post) \
196 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
198 #define MENUOUT(text) \
199 DPRINTF("%s%s", (count++ ? "," : ""), (text))
201 #define MENUFLAG(bit,text) \
202 do { \
203 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
204 } while (0)
206 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
207 const char *postfix)
209 TRACE("%s ", prefix);
210 if (mp) {
211 UINT flags = mp->fType;
212 int typ = MENU_ITEM_TYPE(flags);
213 DPRINTF( "{ ID=0x%x", mp->wID);
214 if (flags & MF_POPUP)
215 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
216 if (flags) {
217 int count = 0;
218 DPRINTF( ", Typ=");
219 if (typ == MFT_STRING)
220 /* Nothing */ ;
221 else if (typ == MFT_SEPARATOR)
222 MENUOUT("sep");
223 else if (typ == MFT_OWNERDRAW)
224 MENUOUT("own");
225 else if (typ == MFT_BITMAP)
226 MENUOUT("bit");
227 else
228 MENUOUT("???");
229 flags -= typ;
231 MENUFLAG(MF_POPUP, "pop");
232 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
233 MENUFLAG(MFT_MENUBREAK, "brk");
234 MENUFLAG(MFT_RADIOCHECK, "radio");
235 MENUFLAG(MFT_RIGHTORDER, "rorder");
236 MENUFLAG(MF_SYSMENU, "sys");
237 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
239 if (flags)
240 DPRINTF( "+0x%x", flags);
242 flags = mp->fState;
243 if (flags) {
244 int count = 0;
245 DPRINTF( ", State=");
246 MENUFLAG(MFS_GRAYED, "grey");
247 MENUFLAG(MFS_DEFAULT, "default");
248 MENUFLAG(MFS_DISABLED, "dis");
249 MENUFLAG(MFS_CHECKED, "check");
250 MENUFLAG(MFS_HILITE, "hi");
251 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
252 MENUFLAG(MF_MOUSESELECT, "mouse");
253 if (flags)
254 DPRINTF( "+0x%x", flags);
256 if (mp->hCheckBit)
257 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
258 if (mp->hUnCheckBit)
259 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
261 if (typ == MFT_STRING) {
262 if (mp->text)
263 DPRINTF( ", Text=%s", debugstr_w(mp->text));
264 else
265 DPRINTF( ", Text=Null");
266 } else if (mp->text == NULL)
267 /* Nothing */ ;
268 else
269 DPRINTF( ", Text=%p", mp->text);
270 if (mp->dwItemData)
271 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
272 DPRINTF( " }");
273 } else {
274 DPRINTF( "NULL");
277 DPRINTF(" %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=%x, 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_FindWndPtr( hwnd );
309 if (win)
311 ret = win->hSysMenu;
312 WIN_ReleaseWndPtr( win );
314 return ret;
317 /***********************************************************************
318 * MENU_CopySysPopup
320 * Return the default system menu.
322 static HMENU MENU_CopySysPopup(void)
324 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
326 if( hMenu ) {
327 POPUPMENU* menu = MENU_GetMenu(hMenu);
328 menu->wFlags |= MF_SYSMENU | MF_POPUP;
329 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
331 else
332 ERR("Unable to load default system menu\n" );
334 TRACE("returning %x.\n", hMenu );
336 return hMenu;
340 /**********************************************************************
341 * MENU_GetSysMenu
343 * Create a copy of the system menu. System menu in Windows is
344 * a special menu bar with the single entry - system menu popup.
345 * This popup is presented to the outside world as a "system menu".
346 * However, the real system menu handle is sometimes seen in the
347 * WM_MENUSELECT parameters (and Word 6 likes it this way).
349 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
351 HMENU hMenu;
353 if ((hMenu = CreateMenu()))
355 POPUPMENU *menu = MENU_GetMenu(hMenu);
356 menu->wFlags = MF_SYSMENU;
357 menu->hWnd = WIN_GetFullHandle( hWnd );
359 if (hPopupMenu == (HMENU)(-1))
360 hPopupMenu = MENU_CopySysPopup();
361 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
363 if (hPopupMenu)
365 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
367 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
368 menu->items[0].fState = 0;
369 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
371 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
372 return hMenu;
374 DestroyMenu( hMenu );
376 ERR("failed to load system menu!\n");
377 return 0;
381 /***********************************************************************
382 * MENU_Init
384 * Menus initialisation.
386 BOOL MENU_Init()
388 HBITMAP hBitmap;
389 NONCLIENTMETRICSA ncm;
391 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
392 0x55, 0, 0xAA, 0,
393 0x55, 0, 0xAA, 0,
394 0x55, 0, 0xAA, 0 };
396 /* Load menu bitmaps */
397 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
398 /* Load system buttons bitmaps */
399 hBmpMinimize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCE));
400 hBmpMinimizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCED));
401 hBmpMaximize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORE));
402 hBmpMaximizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORED));
403 hBmpClose = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSE));
404 hBmpCloseD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSED));
406 if (hStdMnArrow)
408 BITMAP bm;
409 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
410 arrow_bitmap_width = bm.bmWidth;
411 arrow_bitmap_height = bm.bmHeight;
412 } else
413 return FALSE;
415 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
416 return FALSE;
418 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
419 return FALSE;
421 DeleteObject( hBitmap );
422 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
423 return FALSE;
425 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
426 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
427 return FALSE;
429 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
430 return FALSE;
432 ncm.lfMenuFont.lfWeight += 300;
433 if ( ncm.lfMenuFont.lfWeight > 1000)
434 ncm.lfMenuFont.lfWeight = 1000;
436 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
437 return FALSE;
439 return TRUE;
442 /***********************************************************************
443 * MENU_InitSysMenuPopup
445 * Grey the appropriate items in System menu.
447 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
449 BOOL gray;
451 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
452 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
453 gray = ((style & WS_MAXIMIZE) != 0);
454 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
455 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
456 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
457 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
458 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
459 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
460 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
461 gray = (clsStyle & CS_NOCLOSE) != 0;
463 /* The menu item must keep its state if it's disabled */
464 if(gray)
465 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
469 /******************************************************************************
471 * UINT MENU_GetStartOfNextColumn(
472 * HMENU hMenu )
474 *****************************************************************************/
476 static UINT MENU_GetStartOfNextColumn(
477 HMENU hMenu )
479 POPUPMENU *menu = MENU_GetMenu(hMenu);
480 UINT i;
482 if(!menu)
483 return NO_SELECTED_ITEM;
485 i = menu->FocusedItem + 1;
486 if( i == NO_SELECTED_ITEM )
487 return i;
489 for( ; i < menu->nItems; ++i ) {
490 if (menu->items[i].fType & MF_MENUBARBREAK)
491 return i;
494 return NO_SELECTED_ITEM;
498 /******************************************************************************
500 * UINT MENU_GetStartOfPrevColumn(
501 * HMENU hMenu )
503 *****************************************************************************/
505 static UINT MENU_GetStartOfPrevColumn(
506 HMENU hMenu )
508 POPUPMENU *menu = MENU_GetMenu(hMenu);
509 UINT i;
511 if( !menu )
512 return NO_SELECTED_ITEM;
514 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
515 return NO_SELECTED_ITEM;
517 /* Find the start of the column */
519 for(i = menu->FocusedItem; i != 0 &&
520 !(menu->items[i].fType & MF_MENUBARBREAK);
521 --i); /* empty */
523 if(i == 0)
524 return NO_SELECTED_ITEM;
526 for(--i; i != 0; --i) {
527 if (menu->items[i].fType & MF_MENUBARBREAK)
528 break;
531 TRACE("ret %d.\n", i );
533 return i;
538 /***********************************************************************
539 * MENU_FindItem
541 * Find a menu item. Return a pointer on the item, and modifies *hmenu
542 * in case the item was in a sub-menu.
544 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
546 POPUPMENU *menu;
547 UINT i;
549 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
550 if (wFlags & MF_BYPOSITION)
552 if (*nPos >= menu->nItems) return NULL;
553 return &menu->items[*nPos];
555 else
557 MENUITEM *item = menu->items;
558 for (i = 0; i < menu->nItems; i++, item++)
560 if (item->wID == *nPos)
562 *nPos = i;
563 return item;
565 else if (item->fType & MF_POPUP)
567 HMENU hsubmenu = item->hSubMenu;
568 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
569 if (subitem)
571 *hmenu = hsubmenu;
572 return subitem;
577 return NULL;
580 /***********************************************************************
581 * MENU_FindSubMenu
583 * Find a Sub menu. Return the position of the submenu, and modifies
584 * *hmenu in case it is found in another sub-menu.
585 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
587 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
589 POPUPMENU *menu;
590 UINT i;
591 MENUITEM *item;
592 if (((*hmenu)==0xffff) ||
593 (!(menu = MENU_GetMenu(*hmenu))))
594 return NO_SELECTED_ITEM;
595 item = menu->items;
596 for (i = 0; i < menu->nItems; i++, item++) {
597 if(!(item->fType & MF_POPUP)) continue;
598 if (item->hSubMenu == hSubTarget) {
599 return i;
601 else {
602 HMENU hsubmenu = item->hSubMenu;
603 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
604 if (pos != NO_SELECTED_ITEM) {
605 *hmenu = hsubmenu;
606 return pos;
610 return NO_SELECTED_ITEM;
613 /***********************************************************************
614 * MENU_FreeItemData
616 static void MENU_FreeItemData( MENUITEM* item )
618 /* delete text */
619 if (IS_STRING_ITEM(item->fType) && item->text)
620 HeapFree( GetProcessHeap(), 0, item->text );
623 /***********************************************************************
624 * MENU_FindItemByCoords
626 * Find the item at the specified coordinates (screen coords). Does
627 * not work for child windows and therefore should not be called for
628 * an arbitrary system menu.
630 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
631 POINT pt, UINT *pos )
633 MENUITEM *item;
634 UINT i;
635 RECT wrect;
637 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
638 pt.x -= wrect.left;pt.y -= wrect.top;
639 item = menu->items;
640 for (i = 0; i < menu->nItems; i++, item++)
642 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
643 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
645 if (pos) *pos = i;
646 return item;
649 return NULL;
653 /***********************************************************************
654 * MENU_FindItemByKey
656 * Find the menu item selected by a key press.
657 * Return item id, -1 if none, -2 if we should close the menu.
659 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
660 UINT key, BOOL forceMenuChar )
662 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
664 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
666 if (hmenu)
668 POPUPMENU *menu = MENU_GetMenu( hmenu );
669 MENUITEM *item = menu->items;
670 LONG menuchar;
672 if( !forceMenuChar )
674 UINT i;
676 key = toupper(key);
677 for (i = 0; i < menu->nItems; i++, item++)
679 if (item->text && (IS_STRING_ITEM(item->fType)))
681 WCHAR *p = item->text - 2;
684 p = strchrW (p + 2, '&');
686 while (p != NULL && p [1] == '&');
687 if (p && (toupper(p[1]) == key)) return i;
691 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
692 MAKEWPARAM( key, menu->wFlags ), hmenu );
693 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
694 if (HIWORD(menuchar) == 1) return (UINT)(-2);
696 return (UINT)(-1);
698 /***********************************************************************
699 * MENU_LoadMagicItem
701 * Load the bitmap associated with the magic menu item and its style
704 static HBITMAP MENU_LoadMagicItem(UINT id, BOOL hilite, DWORD dwItemData)
707 * Magic menu item id's section
708 * These magic id's are used by windows to insert "standard" mdi
709 * buttons (minimize,restore,close) on menu. Under windows,
710 * these magic id's make sure the right things appear when those
711 * bitmap buttons are pressed/selected/released.
714 switch(id & 0xffff)
715 { case HBMMENU_SYSTEM:
716 return (dwItemData) ?
717 (HBITMAP)dwItemData :
718 (hilite ? hBmpMinimizeD : hBmpMinimize);
719 case HBMMENU_MBAR_RESTORE:
720 return (hilite ? hBmpMaximizeD: hBmpMaximize);
721 case HBMMENU_MBAR_MINIMIZE:
722 return (hilite ? hBmpMinimizeD : hBmpMinimize);
723 case HBMMENU_MBAR_CLOSE:
724 return (hilite ? hBmpCloseD : hBmpClose);
725 case HBMMENU_CALLBACK:
726 case HBMMENU_MBAR_CLOSE_D:
727 case HBMMENU_MBAR_MINIMIZE_D:
728 case HBMMENU_POPUP_CLOSE:
729 case HBMMENU_POPUP_RESTORE:
730 case HBMMENU_POPUP_MAXIMIZE:
731 case HBMMENU_POPUP_MINIMIZE:
732 default:
733 FIXME("Magic 0x%08x not implemented\n", id);
734 return 0;
739 /***********************************************************************
740 * MENU_CalcItemSize
742 * Calculate the size of the menu item and store it in lpitem->rect.
744 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
745 INT orgX, INT orgY, BOOL menuBar )
747 WCHAR *p;
748 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
750 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
751 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
752 (menuBar ? " (MenuBar)" : ""));
754 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
756 if (lpitem->fType & MF_OWNERDRAW)
759 ** Experimentation under Windows reveals that an owner-drawn
760 ** menu is expected to return the size of the content part of
761 ** the menu item, not including the checkmark nor the submenu
762 ** arrow. Windows adds those values itself and returns the
763 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
765 MEASUREITEMSTRUCT mis;
766 mis.CtlType = ODT_MENU;
767 mis.CtlID = 0;
768 mis.itemID = lpitem->wID;
769 mis.itemData = (DWORD)lpitem->dwItemData;
770 mis.itemHeight = 0;
771 mis.itemWidth = 0;
772 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
773 lpitem->rect.right += mis.itemWidth;
775 if (menuBar)
777 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
780 /* under at least win95 you seem to be given a standard
781 height for the menu and the height value is ignored */
783 if (TWEAK_WineLook == WIN31_LOOK)
784 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
785 else
786 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
788 else
789 lpitem->rect.bottom += mis.itemHeight;
791 TRACE("id=%04x size=%dx%d\n",
792 lpitem->wID, mis.itemWidth, mis.itemHeight);
793 /* Fall through to get check/arrow width calculation. */
796 if (lpitem->fType & MF_SEPARATOR)
798 lpitem->rect.bottom += SEPARATOR_HEIGHT;
799 return;
802 if (!menuBar)
804 lpitem->rect.right += 2 * check_bitmap_width;
805 if (lpitem->fType & MF_POPUP)
806 lpitem->rect.right += arrow_bitmap_width;
809 if (lpitem->fType & MF_OWNERDRAW)
810 return;
812 if (IS_BITMAP_ITEM(lpitem->fType))
814 BITMAP bm;
815 HBITMAP resBmp = 0;
817 /* Check if there is a magic menu item associated with this item */
818 if (IS_MAGIC_ITEM(lpitem->text))
820 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fType & MF_HILITE),
821 lpitem->dwItemData);
823 else
824 resBmp = (HBITMAP)lpitem->text;
826 if (GetObjectA(resBmp, sizeof(bm), &bm ))
828 lpitem->rect.right += bm.bmWidth;
829 lpitem->rect.bottom += bm.bmHeight;
830 if (TWEAK_WineLook == WIN98_LOOK) {
831 /* Leave space for the sunken border */
832 lpitem->rect.right += 2;
833 lpitem->rect.bottom += 2;
840 /* it must be a text item - unless it's the system menu */
841 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
842 { SIZE size;
844 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
846 lpitem->rect.right += size.cx;
847 if (TWEAK_WineLook == WIN31_LOOK)
848 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
849 else
850 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
851 lpitem->xTab = 0;
853 if (menuBar)
855 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
857 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
859 /* Item contains a tab (only meaningful in popup menus) */
860 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
861 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
862 lpitem->rect.right += MENU_TAB_SPACE;
864 else
866 if (strchrW( lpitem->text, '\b' ))
867 lpitem->rect.right += MENU_TAB_SPACE;
868 lpitem->xTab = lpitem->rect.right - check_bitmap_width
869 - arrow_bitmap_width;
872 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
876 /***********************************************************************
877 * MENU_PopupMenuCalcSize
879 * Calculate the size of a popup menu.
881 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
883 MENUITEM *lpitem;
884 HDC hdc;
885 int start, i;
886 int orgX, orgY, maxX, maxTab, maxTabWidth;
888 lppop->Width = lppop->Height = 0;
889 if (lppop->nItems == 0) return;
890 hdc = GetDC( 0 );
892 SelectObject( hdc, hMenuFont);
894 start = 0;
895 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
897 while (start < lppop->nItems)
899 lpitem = &lppop->items[start];
900 orgX = maxX;
901 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
903 maxTab = maxTabWidth = 0;
905 /* Parse items until column break or end of menu */
906 for (i = start; i < lppop->nItems; i++, lpitem++)
908 if ((i != start) &&
909 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
911 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
913 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
914 maxX = max( maxX, lpitem->rect.right );
915 orgY = lpitem->rect.bottom;
916 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
918 maxTab = max( maxTab, lpitem->xTab );
919 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
923 /* Finish the column (set all items to the largest width found) */
924 maxX = max( maxX, maxTab + maxTabWidth );
925 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
927 lpitem->rect.right = maxX;
928 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
929 lpitem->xTab = maxTab;
932 lppop->Height = max( lppop->Height, orgY );
935 lppop->Width = maxX;
937 /* space for 3d border */
938 if(TWEAK_WineLook > WIN31_LOOK)
940 lppop->Height += 2;
941 lppop->Width += 2;
944 ReleaseDC( 0, hdc );
948 /***********************************************************************
949 * MENU_MenuBarCalcSize
951 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
952 * height is off by 1 pixel which causes lengthy window relocations when
953 * active document window is maximized/restored.
955 * Calculate the size of the menu bar.
957 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
958 LPPOPUPMENU lppop, HWND hwndOwner )
960 MENUITEM *lpitem;
961 int start, i, orgX, orgY, maxY, helpPos;
963 if ((lprect == NULL) || (lppop == NULL)) return;
964 if (lppop->nItems == 0) return;
965 TRACE("left=%d top=%d right=%d bottom=%d\n",
966 lprect->left, lprect->top, lprect->right, lprect->bottom);
967 lppop->Width = lprect->right - lprect->left;
968 lppop->Height = 0;
969 maxY = lprect->top+1;
970 start = 0;
971 helpPos = -1;
972 while (start < lppop->nItems)
974 lpitem = &lppop->items[start];
975 orgX = lprect->left;
976 orgY = maxY;
978 /* Parse items until line break or end of menu */
979 for (i = start; i < lppop->nItems; i++, lpitem++)
981 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
982 if ((i != start) &&
983 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
985 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
986 orgX, orgY );
987 debug_print_menuitem (" item: ", lpitem, "");
988 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
990 if (lpitem->rect.right > lprect->right)
992 if (i != start) break;
993 else lpitem->rect.right = lprect->right;
995 maxY = max( maxY, lpitem->rect.bottom );
996 orgX = lpitem->rect.right;
999 /* Finish the line (set all items to the largest height found) */
1000 while (start < i) lppop->items[start++].rect.bottom = maxY;
1003 lprect->bottom = maxY;
1004 lppop->Height = lprect->bottom - lprect->top;
1006 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1007 /* the last item (if several lines, only move the last line) */
1008 lpitem = &lppop->items[lppop->nItems-1];
1009 orgY = lpitem->rect.top;
1010 orgX = lprect->right;
1011 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1012 if ( (helpPos==-1) || (helpPos>i) )
1013 break; /* done */
1014 if (lpitem->rect.top != orgY) break; /* Other line */
1015 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1016 lpitem->rect.left += orgX - lpitem->rect.right;
1017 lpitem->rect.right = orgX;
1018 orgX = lpitem->rect.left;
1022 /***********************************************************************
1023 * MENU_DrawMenuItem
1025 * Draw a single menu item.
1027 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1028 UINT height, BOOL menuBar, UINT odaction )
1030 RECT rect;
1032 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1034 if (lpitem->fType & MF_SYSMENU)
1036 if( !IsIconic(hwnd) ) {
1037 if (TWEAK_WineLook > WIN31_LOOK)
1038 NC_DrawSysButton95( hwnd, hdc,
1039 lpitem->fState &
1040 (MF_HILITE | MF_MOUSESELECT) );
1041 else
1042 NC_DrawSysButton( hwnd, hdc,
1043 lpitem->fState &
1044 (MF_HILITE | MF_MOUSESELECT) );
1047 return;
1050 if (lpitem->fType & MF_OWNERDRAW)
1053 ** Experimentation under Windows reveals that an owner-drawn
1054 ** menu is given the rectangle which includes the space it requested
1055 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1056 ** and a popup-menu arrow. This is the value of lpitem->rect.
1057 ** Windows will leave all drawing to the application except for
1058 ** the popup-menu arrow. Windows always draws that itself, after
1059 ** the menu owner has finished drawing.
1061 DRAWITEMSTRUCT dis;
1063 dis.CtlType = ODT_MENU;
1064 dis.CtlID = 0;
1065 dis.itemID = lpitem->wID;
1066 dis.itemData = (DWORD)lpitem->dwItemData;
1067 dis.itemState = 0;
1068 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1069 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED;
1070 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1071 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1072 dis.hwndItem = (HWND)hmenu;
1073 dis.hDC = hdc;
1074 dis.rcItem = lpitem->rect;
1075 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1076 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1077 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1078 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1079 dis.rcItem.bottom);
1080 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1081 /* Fall through to draw popup-menu arrow */
1084 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1085 lpitem->rect.right,lpitem->rect.bottom);
1087 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1089 rect = lpitem->rect;
1091 if (!(lpitem->fType & MF_OWNERDRAW))
1093 if (lpitem->fState & MF_HILITE)
1095 if(TWEAK_WineLook == WIN98_LOOK)
1097 if(menuBar)
1098 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1099 else
1100 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1102 else /* Not Win98 Look */
1104 if(!IS_BITMAP_ITEM(lpitem->fType))
1105 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1108 else
1109 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1112 SetBkMode( hdc, TRANSPARENT );
1114 if (!(lpitem->fType & MF_OWNERDRAW))
1116 /* vertical separator */
1117 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1119 if (TWEAK_WineLook > WIN31_LOOK)
1121 RECT rc = rect;
1122 rc.top = 3;
1123 rc.bottom = height - 3;
1124 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1126 else
1128 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1129 MoveToEx( hdc, rect.left, 0, NULL );
1130 LineTo( hdc, rect.left, height );
1134 /* horizontal separator */
1135 if (lpitem->fType & MF_SEPARATOR)
1137 if (TWEAK_WineLook > WIN31_LOOK)
1139 RECT rc = rect;
1140 rc.left++;
1141 rc.right--;
1142 rc.top += SEPARATOR_HEIGHT / 2;
1143 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1145 else
1147 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1148 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1149 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1151 return;
1155 /* Setup colors */
1157 if (lpitem->fState & MF_HILITE)
1159 if(TWEAK_WineLook == WIN98_LOOK)
1161 if(menuBar) {
1162 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1163 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1164 } else {
1165 if(lpitem->fState & MF_GRAYED)
1166 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1167 else
1168 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1169 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1172 else /* Not Win98 Look */
1174 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1175 if(!IS_BITMAP_ITEM(lpitem->fType))
1176 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1179 else
1181 if (lpitem->fState & MF_GRAYED)
1182 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1183 else
1184 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1185 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1188 /* helper lines for debugging */
1189 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1190 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1191 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1192 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1195 if (!menuBar)
1197 INT y = rect.top + rect.bottom;
1198 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1199 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1201 if (!(lpitem->fType & MF_OWNERDRAW))
1203 /* Draw the check mark
1205 * FIXME:
1206 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1208 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1209 if (bm) /* we have a custom bitmap */
1211 HDC hdcMem = CreateCompatibleDC( hdc );
1212 SelectObject( hdcMem, bm );
1213 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1214 check_bitmap_width, check_bitmap_height,
1215 hdcMem, 0, 0, SRCCOPY );
1216 DeleteDC( hdcMem );
1218 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1220 RECT r;
1221 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1222 HDC hdcMem = CreateCompatibleDC( hdc );
1223 SelectObject( hdcMem, bm );
1224 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1225 DrawFrameControl( hdcMem, &r, DFC_MENU,
1226 (lpitem->fType & MFT_RADIOCHECK) ?
1227 DFCS_MENUBULLET : DFCS_MENUCHECK );
1228 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1229 hdcMem, 0, 0, SRCCOPY );
1230 DeleteDC( hdcMem );
1231 DeleteObject( bm );
1235 /* Draw the popup-menu arrow */
1236 if (lpitem->fType & MF_POPUP)
1238 HDC hdcMem = CreateCompatibleDC( hdc );
1239 HBITMAP hOrigBitmap;
1241 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1242 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1243 (y - arrow_bitmap_height) / 2,
1244 arrow_bitmap_width, arrow_bitmap_height,
1245 hdcMem, 0, 0, SRCCOPY );
1246 SelectObject( hdcMem, hOrigBitmap );
1247 DeleteDC( hdcMem );
1250 rect.left += check_bitmap_width;
1251 rect.right -= arrow_bitmap_width;
1254 /* Done for owner-drawn */
1255 if (lpitem->fType & MF_OWNERDRAW)
1256 return;
1258 /* Draw the item text or bitmap */
1259 if (IS_BITMAP_ITEM(lpitem->fType))
1261 int left,top,w,h;
1262 DWORD rop;
1264 HBITMAP resBmp = 0;
1266 HDC hdcMem = CreateCompatibleDC( hdc );
1269 * Check if there is a magic menu item associated with this item
1270 * and load the appropriate bitmap
1272 if (IS_MAGIC_ITEM(lpitem->text))
1274 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fState & MF_HILITE),
1275 lpitem->dwItemData);
1277 else
1278 resBmp = (HBITMAP)lpitem->text;
1280 if (resBmp)
1282 BITMAP bm;
1283 GetObjectA( resBmp, sizeof(bm), &bm );
1285 SelectObject(hdcMem,resBmp );
1287 /* handle fontsize > bitmap_height */
1288 h=rect.bottom - rect.top;
1289 top = (h>bm.bmHeight) ?
1290 rect.top+(h-bm.bmHeight)/2 : rect.top;
1291 w=rect.right - rect.left;
1292 left=rect.left;
1293 if (TWEAK_WineLook == WIN95_LOOK) {
1294 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
1295 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
1296 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1297 } else {
1298 left++;
1299 w-=2;
1300 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
1302 BitBlt( hdc, left, top, w,
1303 h, hdcMem, 0, 0,
1304 rop);
1306 DeleteDC( hdcMem );
1308 return;
1311 /* No bitmap - process text if present */
1312 else if (IS_STRING_ITEM(lpitem->fType))
1314 register int i;
1315 HFONT hfontOld = 0;
1317 UINT uFormat = (menuBar) ?
1318 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1319 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1321 if ( lpitem->fState & MFS_DEFAULT )
1323 hfontOld = SelectObject( hdc, hMenuFontBold);
1326 if (menuBar)
1328 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1329 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1330 i = strlenW( lpitem->text );
1332 else
1334 for (i = 0; lpitem->text[i]; i++)
1335 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1336 break;
1339 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1341 if (!(lpitem->fState & MF_HILITE) )
1343 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1344 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1345 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1346 --rect.left; --rect.top; --rect.right; --rect.bottom;
1348 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1351 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1353 /* paint the shortcut text */
1354 if (lpitem->text[i]) /* There's a tab or flush-right char */
1356 if (lpitem->text[i] == '\t')
1358 rect.left = lpitem->xTab;
1359 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1361 else
1363 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1366 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1368 if (!(lpitem->fState & MF_HILITE) )
1370 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1371 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1372 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1373 --rect.left; --rect.top; --rect.right; --rect.bottom;
1375 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1377 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1380 if (hfontOld)
1381 SelectObject (hdc, hfontOld);
1386 /***********************************************************************
1387 * MENU_DrawPopupMenu
1389 * Paint a popup menu.
1391 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1393 HBRUSH hPrevBrush = 0;
1394 RECT rect;
1396 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1398 GetClientRect( hwnd, &rect );
1400 if(TWEAK_WineLook == WIN31_LOOK)
1402 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1403 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1406 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1407 && (SelectObject( hdc, hMenuFont)))
1409 HPEN hPrevPen;
1411 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1413 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1414 if( hPrevPen )
1416 INT ropPrev, i;
1417 POPUPMENU *menu;
1419 /* draw 3-d shade */
1420 if(TWEAK_WineLook == WIN31_LOOK) {
1421 SelectObject( hdc, hShadeBrush );
1422 SetBkMode( hdc, TRANSPARENT );
1423 ropPrev = SetROP2( hdc, R2_MASKPEN );
1425 i = rect.right; /* why SetBrushOrg() doesn't? */
1426 PatBlt( hdc, i & 0xfffffffe,
1427 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1428 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1429 rect.bottom - rect.top, 0x00a000c9 );
1430 i = rect.bottom;
1431 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1432 i & 0xfffffffe,rect.right - rect.left,
1433 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1434 SelectObject( hdc, hPrevPen );
1435 SelectObject( hdc, hPrevBrush );
1436 SetROP2( hdc, ropPrev );
1438 else
1439 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1441 /* draw menu items */
1443 menu = MENU_GetMenu( hmenu );
1444 if (menu && menu->nItems)
1446 MENUITEM *item;
1447 UINT u;
1449 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1450 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1451 menu->Height, FALSE, ODA_DRAWENTIRE );
1454 } else
1456 SelectObject( hdc, hPrevBrush );
1461 /***********************************************************************
1462 * MENU_DrawMenuBar
1464 * Paint a menu bar. Returns the height of the menu bar.
1465 * called from [windows/nonclient.c]
1467 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1468 BOOL suppress_draw)
1470 LPPOPUPMENU lppop;
1471 UINT i,retvalue;
1472 HFONT hfontOld = 0;
1473 HMENU hMenu = GetMenu(hwnd);
1475 lppop = MENU_GetMenu( hMenu );
1476 if (lppop == NULL || lprect == NULL)
1478 retvalue = GetSystemMetrics(SM_CYMENU);
1479 goto END;
1482 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1484 hfontOld = SelectObject( hDC, hMenuFont);
1486 if (lppop->Height == 0)
1487 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1489 lprect->bottom = lprect->top + lppop->Height;
1491 if (suppress_draw)
1493 retvalue = lppop->Height;
1494 goto END;
1497 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1499 if (TWEAK_WineLook == WIN31_LOOK)
1501 SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1502 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1503 LineTo( hDC, lprect->right, lprect->bottom );
1505 else
1507 SelectObject( hDC, GetSysColorPen(COLOR_3DFACE));
1508 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1509 LineTo( hDC, lprect->right, lprect->bottom );
1512 if (lppop->nItems == 0)
1514 retvalue = GetSystemMetrics(SM_CYMENU);
1515 goto END;
1518 for (i = 0; i < lppop->nItems; i++)
1520 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
1521 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1523 retvalue = lppop->Height;
1525 END:
1526 if (hfontOld) SelectObject (hDC, hfontOld);
1527 return retvalue;
1531 /***********************************************************************
1532 * MENU_ShowPopup
1534 * Display a popup menu.
1536 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1537 INT x, INT y, INT xanchor, INT yanchor )
1539 POPUPMENU *menu;
1540 UINT width, height;
1542 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1543 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1545 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1546 if (menu->FocusedItem != NO_SELECTED_ITEM)
1548 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1549 menu->FocusedItem = NO_SELECTED_ITEM;
1552 /* store the owner for DrawItem */
1553 menu->hwndOwner = hwndOwner;
1556 MENU_PopupMenuCalcSize( menu, hwndOwner );
1558 /* adjust popup menu pos so that it fits within the desktop */
1560 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1561 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1563 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1565 if( xanchor )
1566 x -= width - xanchor;
1567 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1568 x = GetSystemMetrics(SM_CXSCREEN) - width;
1570 if( x < 0 ) x = 0;
1572 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1574 if( yanchor )
1575 y -= height + yanchor;
1576 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1577 y = GetSystemMetrics(SM_CYSCREEN) - height;
1579 if( y < 0 ) y = 0;
1581 if( TWEAK_WineLook == WIN31_LOOK )
1583 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1584 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1587 /* NOTE: In Windows, top menu popup is not owned. */
1588 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1589 WS_POPUP, x, y, width, height,
1590 hwndOwner, 0, GetWindowLongA(hwndOwner,GWL_HINSTANCE),
1591 (LPVOID)hmenu );
1592 if( !menu->hWnd ) return FALSE;
1593 if (!top_popup) top_popup = menu->hWnd;
1595 /* Display the window */
1597 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1598 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1599 UpdateWindow( menu->hWnd );
1600 return TRUE;
1604 /***********************************************************************
1605 * MENU_SelectItem
1607 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1608 BOOL sendMenuSelect, HMENU topmenu )
1610 LPPOPUPMENU lppop;
1611 HDC hdc;
1613 TRACE("owner=0x%04x menu=0x%04x index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1615 lppop = MENU_GetMenu( hmenu );
1616 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1618 if (lppop->FocusedItem == wIndex) return;
1619 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1620 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1622 SelectObject( hdc, hMenuFont);
1624 /* Clear previous highlighted item */
1625 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1627 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1628 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1629 lppop->Height, !(lppop->wFlags & MF_POPUP),
1630 ODA_SELECT );
1633 /* Highlight new item (if any) */
1634 lppop->FocusedItem = wIndex;
1635 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1637 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1638 lppop->items[wIndex].fState |= MF_HILITE;
1639 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1640 &lppop->items[wIndex], lppop->Height,
1641 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1643 if (sendMenuSelect)
1645 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1646 SendMessageA( hwndOwner, WM_MENUSELECT,
1647 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1648 ip->fType | ip->fState | MF_MOUSESELECT |
1649 (lppop->wFlags & MF_SYSMENU)), hmenu);
1652 else if (sendMenuSelect) {
1653 if(topmenu){
1654 int pos;
1655 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1656 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1657 MENUITEM *ip = &ptm->items[pos];
1658 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1659 ip->fType | ip->fState | MF_MOUSESELECT |
1660 (ptm->wFlags & MF_SYSMENU)), topmenu);
1664 ReleaseDC( lppop->hWnd, hdc );
1668 /***********************************************************************
1669 * MENU_MoveSelection
1671 * Moves currently selected item according to the offset parameter.
1672 * If there is no selection then it should select the last item if
1673 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1675 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1677 INT i;
1678 POPUPMENU *menu;
1680 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1682 menu = MENU_GetMenu( hmenu );
1683 if ((!menu) || (!menu->items)) return;
1685 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1687 if( menu->nItems == 1 ) return; else
1688 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1689 ; i += offset)
1690 if (!(menu->items[i].fType & MF_SEPARATOR))
1692 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1693 return;
1697 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1698 i >= 0 && i < menu->nItems ; i += offset)
1699 if (!(menu->items[i].fType & MF_SEPARATOR))
1701 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1702 return;
1707 /**********************************************************************
1708 * MENU_SetItemData
1710 * Set an item flags, id and text ptr. Called by InsertMenu() and
1711 * ModifyMenu().
1713 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1714 LPCWSTR str )
1716 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1718 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1719 TRACE("flags=%x str=%p\n", flags, str);
1721 if (IS_STRING_ITEM(flags))
1723 if (!str)
1725 flags |= MF_SEPARATOR;
1726 item->text = NULL;
1728 else
1730 LPWSTR text;
1731 /* Item beginning with a backspace is a help item */
1732 if (*str == '\b')
1734 flags |= MF_HELP;
1735 str++;
1737 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1738 return FALSE;
1739 strcpyW( text, str );
1740 item->text = text;
1743 else if (IS_BITMAP_ITEM(flags))
1744 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1745 else item->text = NULL;
1747 if (flags & MF_OWNERDRAW)
1748 item->dwItemData = (DWORD)str;
1749 else
1750 item->dwItemData = 0;
1752 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1753 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1755 if (flags & MF_POPUP)
1757 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1758 if (menu) menu->wFlags |= MF_POPUP;
1759 else
1761 item->wID = 0;
1762 item->hSubMenu = 0;
1763 item->fType = 0;
1764 item->fState = 0;
1765 return FALSE;
1769 item->wID = id;
1770 if (flags & MF_POPUP)
1771 item->hSubMenu = id;
1773 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1774 flags |= MF_POPUP; /* keep popup */
1776 item->fType = flags & TYPE_MASK;
1777 item->fState = (flags & STATE_MASK) &
1778 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1781 /* Don't call SetRectEmpty here! */
1784 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1786 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1787 return TRUE;
1791 /**********************************************************************
1792 * MENU_InsertItem
1794 * Insert a new item into a menu.
1796 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1798 MENUITEM *newItems;
1799 POPUPMENU *menu;
1801 if (!(menu = MENU_GetMenu(hMenu)))
1802 return NULL;
1804 /* Find where to insert new item */
1806 if (flags & MF_BYPOSITION) {
1807 if (pos > menu->nItems)
1808 pos = menu->nItems;
1809 } else {
1810 if (!MENU_FindItem( &hMenu, &pos, flags ))
1811 pos = menu->nItems;
1812 else {
1813 if (!(menu = MENU_GetMenu( hMenu )))
1814 return NULL;
1818 /* Create new items array */
1820 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1821 if (!newItems)
1823 WARN("allocation failed\n" );
1824 return NULL;
1826 if (menu->nItems > 0)
1828 /* Copy the old array into the new one */
1829 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1830 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1831 (menu->nItems-pos)*sizeof(MENUITEM) );
1832 HeapFree( GetProcessHeap(), 0, menu->items );
1834 menu->items = newItems;
1835 menu->nItems++;
1836 memset( &newItems[pos], 0, sizeof(*newItems) );
1837 menu->Height = 0; /* force size recalculate */
1838 return &newItems[pos];
1842 /**********************************************************************
1843 * MENU_ParseResource
1845 * Parse a standard menu resource and add items to the menu.
1846 * Return a pointer to the end of the resource.
1848 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1850 WORD flags, id = 0;
1851 LPCSTR str;
1855 flags = GET_WORD(res);
1856 res += sizeof(WORD);
1857 if (!(flags & MF_POPUP))
1859 id = GET_WORD(res);
1860 res += sizeof(WORD);
1862 if (!IS_STRING_ITEM(flags))
1863 ERR("not a string item %04x\n", flags );
1864 str = res;
1865 if (!unicode) res += strlen(str) + 1;
1866 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1867 if (flags & MF_POPUP)
1869 HMENU hSubMenu = CreatePopupMenu();
1870 if (!hSubMenu) return NULL;
1871 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1872 return NULL;
1873 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1874 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1876 else /* Not a popup */
1878 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1879 else AppendMenuW( hMenu, flags, id,
1880 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1882 } while (!(flags & MF_END));
1883 return res;
1887 /**********************************************************************
1888 * MENUEX_ParseResource
1890 * Parse an extended menu resource and add items to the menu.
1891 * Return a pointer to the end of the resource.
1893 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1895 WORD resinfo;
1896 do {
1897 MENUITEMINFOW mii;
1899 mii.cbSize = sizeof(mii);
1900 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1901 mii.fType = GET_DWORD(res);
1902 res += sizeof(DWORD);
1903 mii.fState = GET_DWORD(res);
1904 res += sizeof(DWORD);
1905 mii.wID = GET_DWORD(res);
1906 res += sizeof(DWORD);
1907 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1908 res += sizeof(WORD);
1909 /* Align the text on a word boundary. */
1910 res += (~((int)res - 1)) & 1;
1911 mii.dwTypeData = (LPWSTR) res;
1912 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1913 /* Align the following fields on a dword boundary. */
1914 res += (~((int)res - 1)) & 3;
1916 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1917 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1919 if (resinfo & 1) { /* Pop-up? */
1920 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1921 res += sizeof(DWORD);
1922 mii.hSubMenu = CreatePopupMenu();
1923 if (!mii.hSubMenu)
1924 return NULL;
1925 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1926 DestroyMenu(mii.hSubMenu);
1927 return NULL;
1929 mii.fMask |= MIIM_SUBMENU;
1930 mii.fType |= MF_POPUP;
1932 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1933 } while (!(resinfo & MF_END));
1934 return res;
1938 /***********************************************************************
1939 * MENU_GetSubPopup
1941 * Return the handle of the selected sub-popup menu (if any).
1943 static HMENU MENU_GetSubPopup( HMENU hmenu )
1945 POPUPMENU *menu;
1946 MENUITEM *item;
1948 menu = MENU_GetMenu( hmenu );
1950 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1952 item = &menu->items[menu->FocusedItem];
1953 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1954 return item->hSubMenu;
1955 return 0;
1959 /***********************************************************************
1960 * MENU_HideSubPopups
1962 * Hide the sub-popup menus of this menu.
1964 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1965 BOOL sendMenuSelect )
1967 POPUPMENU *menu = MENU_GetMenu( hmenu );
1969 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1971 if (menu && top_popup)
1973 HMENU hsubmenu;
1974 POPUPMENU *submenu;
1975 MENUITEM *item;
1977 if (menu->FocusedItem != NO_SELECTED_ITEM)
1979 item = &menu->items[menu->FocusedItem];
1980 if (!(item->fType & MF_POPUP) ||
1981 !(item->fState & MF_MOUSESELECT)) return;
1982 item->fState &= ~MF_MOUSESELECT;
1983 hsubmenu = item->hSubMenu;
1984 } else return;
1986 submenu = MENU_GetMenu( hsubmenu );
1987 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1988 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1989 DestroyWindow( submenu->hWnd );
1990 submenu->hWnd = 0;
1995 /***********************************************************************
1996 * MENU_ShowSubPopup
1998 * Display the sub-menu of the selected item of this menu.
1999 * Return the handle of the submenu, or hmenu if no submenu to display.
2001 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2002 BOOL selectFirst, UINT wFlags )
2004 RECT rect;
2005 POPUPMENU *menu;
2006 MENUITEM *item;
2007 HDC hdc;
2009 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2011 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2013 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2015 item = &menu->items[menu->FocusedItem];
2016 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2017 return hmenu;
2019 /* message must be sent before using item,
2020 because nearly everything may be changed by the application ! */
2022 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2023 if (!(wFlags & TPM_NONOTIFY))
2024 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2025 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2027 item = &menu->items[menu->FocusedItem];
2028 rect = item->rect;
2030 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2031 if (!(item->fState & MF_HILITE))
2033 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2034 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2036 SelectObject( hdc, hMenuFont);
2038 item->fState |= MF_HILITE;
2039 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2040 ReleaseDC( menu->hWnd, hdc );
2042 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2043 item->rect = rect;
2045 item->fState |= MF_MOUSESELECT;
2047 if (IS_SYSTEM_MENU(menu))
2049 MENU_InitSysMenuPopup(item->hSubMenu,
2050 GetWindowLongA( menu->hWnd, GWL_STYLE ),
2051 GetClassLongA( menu->hWnd, GCL_STYLE));
2053 NC_GetSysPopupPos( menu->hWnd, &rect );
2054 rect.top = rect.bottom;
2055 rect.right = GetSystemMetrics(SM_CXSIZE);
2056 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2058 else
2060 GetWindowRect( menu->hWnd, &rect );
2061 if (menu->wFlags & MF_POPUP)
2063 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2064 rect.top += item->rect.top;
2065 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2066 rect.bottom = item->rect.top - item->rect.bottom;
2068 else
2070 rect.left += item->rect.left;
2071 rect.top += item->rect.bottom;
2072 rect.right = item->rect.right - item->rect.left;
2073 rect.bottom = item->rect.bottom - item->rect.top;
2077 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2078 rect.left, rect.top, rect.right, rect.bottom );
2079 if (selectFirst)
2080 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2081 return item->hSubMenu;
2086 /**********************************************************************
2087 * MENU_IsMenuActive
2089 BOOL MENU_IsMenuActive(void)
2091 return (top_popup != 0);
2094 /***********************************************************************
2095 * MENU_PtMenu
2097 * Walks menu chain trying to find a menu pt maps to.
2099 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2101 POPUPMENU *menu = MENU_GetMenu( hMenu );
2102 UINT item = menu->FocusedItem;
2103 HMENU ret;
2105 /* try subpopup first (if any) */
2106 ret = (item != NO_SELECTED_ITEM &&
2107 (menu->items[item].fType & MF_POPUP) &&
2108 (menu->items[item].fState & MF_MOUSESELECT))
2109 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2111 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2113 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2114 if( menu->wFlags & MF_POPUP )
2116 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2118 else if (ht == HTSYSMENU)
2119 ret = get_win_sys_menu( menu->hWnd );
2120 else if (ht == HTMENU)
2121 ret = GetMenu( menu->hWnd );
2123 return ret;
2126 /***********************************************************************
2127 * MENU_ExecFocusedItem
2129 * Execute a menu item (for instance when user pressed Enter).
2130 * Return the wID of the executed item. Otherwise, -1 indicating
2131 * that no menu item was executed;
2132 * Have to receive the flags for the TrackPopupMenu options to avoid
2133 * sending unwanted message.
2136 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2138 MENUITEM *item;
2139 POPUPMENU *menu = MENU_GetMenu( hMenu );
2141 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2143 if (!menu || !menu->nItems ||
2144 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2146 item = &menu->items[menu->FocusedItem];
2148 TRACE("%08x %08x %08x\n",
2149 hMenu, item->wID, item->hSubMenu);
2151 if (!(item->fType & MF_POPUP))
2153 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2155 /* If TPM_RETURNCMD is set you return the id, but
2156 do not send a message to the owner */
2157 if(!(wFlags & TPM_RETURNCMD))
2159 if( menu->wFlags & MF_SYSMENU )
2160 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2161 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2162 else
2163 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2165 return item->wID;
2168 else
2169 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2171 return -1;
2174 /***********************************************************************
2175 * MENU_SwitchTracking
2177 * Helper function for menu navigation routines.
2179 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2181 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2182 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2184 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2186 if( pmt->hTopMenu != hPtMenu &&
2187 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2189 /* both are top level menus (system and menu-bar) */
2190 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2191 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2192 pmt->hTopMenu = hPtMenu;
2194 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2195 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2199 /***********************************************************************
2200 * MENU_ButtonDown
2202 * Return TRUE if we can go on with menu tracking.
2204 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2206 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2208 if (hPtMenu)
2210 UINT id = 0;
2211 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2212 MENUITEM *item;
2214 if( IS_SYSTEM_MENU(ptmenu) )
2215 item = ptmenu->items;
2216 else
2217 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2219 if( item )
2221 if( ptmenu->FocusedItem != id )
2222 MENU_SwitchTracking( pmt, hPtMenu, id );
2224 /* If the popup menu is not already "popped" */
2225 if(!(item->fState & MF_MOUSESELECT ))
2227 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2229 /* In win31, a newly popped menu always remains opened for the next buttonup */
2230 if(TWEAK_WineLook == WIN31_LOOK)
2231 ptmenu->bTimeToHide = FALSE;
2234 return TRUE;
2236 /* Else the click was on the menu bar, finish the tracking */
2238 return FALSE;
2241 /***********************************************************************
2242 * MENU_ButtonUp
2244 * Return the value of MENU_ExecFocusedItem if
2245 * the selected item was not a popup. Else open the popup.
2246 * A -1 return value indicates that we go on with menu tracking.
2249 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2251 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2253 if (hPtMenu)
2255 UINT id = 0;
2256 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2257 MENUITEM *item;
2259 if( IS_SYSTEM_MENU(ptmenu) )
2260 item = ptmenu->items;
2261 else
2262 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2264 if( item && (ptmenu->FocusedItem == id ))
2266 if( !(item->fType & MF_POPUP) )
2267 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2269 /* If we are dealing with the top-level menu */
2270 /* and this is a click on an already "popped" item: */
2271 /* Stop the menu tracking and close the opened submenus */
2272 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2273 return 0;
2275 ptmenu->bTimeToHide = TRUE;
2277 return -1;
2281 /***********************************************************************
2282 * MENU_MouseMove
2284 * Return TRUE if we can go on with menu tracking.
2286 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2288 UINT id = NO_SELECTED_ITEM;
2289 POPUPMENU *ptmenu = NULL;
2291 if( hPtMenu )
2293 ptmenu = MENU_GetMenu( hPtMenu );
2294 if( IS_SYSTEM_MENU(ptmenu) )
2295 id = 0;
2296 else
2297 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2300 if( id == NO_SELECTED_ITEM )
2302 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2303 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2306 else if( ptmenu->FocusedItem != id )
2308 MENU_SwitchTracking( pmt, hPtMenu, id );
2309 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2311 return TRUE;
2315 /***********************************************************************
2316 * MENU_DoNextMenu
2318 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2320 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2322 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2324 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2325 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2327 MDINEXTMENU next_menu;
2328 HMENU hNewMenu;
2329 HWND hNewWnd;
2330 UINT id = 0;
2332 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2333 next_menu.hmenuNext = 0;
2334 next_menu.hwndNext = 0;
2335 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2337 TRACE("%04x [%04x] -> %04x [%04x]\n",
2338 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2340 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2342 DWORD style = GetWindowLongA( pmt->hOwnerWnd, GWL_STYLE );
2343 hNewWnd = pmt->hOwnerWnd;
2344 if( IS_SYSTEM_MENU(menu) )
2346 /* switch to the menu bar */
2348 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2350 if( vk == VK_LEFT )
2352 menu = MENU_GetMenu( hNewMenu );
2353 id = menu->nItems - 1;
2356 else if (style & WS_SYSMENU )
2358 /* switch to the system menu */
2359 hNewMenu = get_win_sys_menu( hNewWnd );
2361 else return FALSE;
2363 else /* application returned a new menu to switch to */
2365 hNewMenu = next_menu.hmenuNext;
2366 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2368 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2370 DWORD style = GetWindowLongA( hNewWnd, GWL_STYLE );
2372 if (style & WS_SYSMENU &&
2373 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2375 /* get the real system menu */
2376 hNewMenu = get_win_sys_menu(hNewWnd);
2378 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2380 /* FIXME: Not sure what to do here;
2381 * perhaps try to track hNewMenu as a popup? */
2383 TRACE(" -- got confused.\n");
2384 return FALSE;
2387 else return FALSE;
2390 if( hNewMenu != pmt->hTopMenu )
2392 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2393 FALSE, 0 );
2394 if( pmt->hCurrentMenu != pmt->hTopMenu )
2395 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2398 if( hNewWnd != pmt->hOwnerWnd )
2400 ReleaseCapture();
2401 pmt->hOwnerWnd = hNewWnd;
2402 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2405 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2406 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2408 return TRUE;
2410 return FALSE;
2413 /***********************************************************************
2414 * MENU_SuspendPopup
2416 * The idea is not to show the popup if the next input message is
2417 * going to hide it anyway.
2419 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2421 MSG msg;
2423 msg.hwnd = pmt->hOwnerWnd;
2425 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2426 pmt->trackFlags |= TF_SKIPREMOVE;
2428 switch( uMsg )
2430 case WM_KEYDOWN:
2431 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2432 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2434 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2435 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2436 if( msg.message == WM_KEYDOWN &&
2437 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2439 pmt->trackFlags |= TF_SUSPENDPOPUP;
2440 return TRUE;
2443 break;
2446 /* failures go through this */
2447 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2448 return FALSE;
2451 /***********************************************************************
2452 * MENU_KeyEscape
2454 * Handle a VK_ESCAPE key event in a menu.
2456 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2458 BOOL bEndMenu = TRUE;
2460 if (pmt->hCurrentMenu != pmt->hTopMenu)
2462 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2464 if (menu->wFlags & MF_POPUP)
2466 HMENU hmenutmp, hmenuprev;
2468 hmenuprev = hmenutmp = pmt->hTopMenu;
2470 /* close topmost popup */
2471 while (hmenutmp != pmt->hCurrentMenu)
2473 hmenuprev = hmenutmp;
2474 hmenutmp = MENU_GetSubPopup( hmenuprev );
2477 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2478 pmt->hCurrentMenu = hmenuprev;
2479 bEndMenu = FALSE;
2483 return bEndMenu;
2486 /***********************************************************************
2487 * MENU_KeyLeft
2489 * Handle a VK_LEFT key event in a menu.
2491 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2493 POPUPMENU *menu;
2494 HMENU hmenutmp, hmenuprev;
2495 UINT prevcol;
2497 hmenuprev = hmenutmp = pmt->hTopMenu;
2498 menu = MENU_GetMenu( hmenutmp );
2500 /* Try to move 1 column left (if possible) */
2501 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2502 NO_SELECTED_ITEM ) {
2504 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2505 prevcol, TRUE, 0 );
2506 return;
2509 /* close topmost popup */
2510 while (hmenutmp != pmt->hCurrentMenu)
2512 hmenuprev = hmenutmp;
2513 hmenutmp = MENU_GetSubPopup( hmenuprev );
2516 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2517 pmt->hCurrentMenu = hmenuprev;
2519 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2521 /* move menu bar selection if no more popups are left */
2523 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2524 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2526 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2528 /* A sublevel menu was displayed - display the next one
2529 * unless there is another displacement coming up */
2531 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2532 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2533 pmt->hTopMenu, TRUE, wFlags);
2539 /***********************************************************************
2540 * MENU_KeyRight
2542 * Handle a VK_RIGHT key event in a menu.
2544 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2546 HMENU hmenutmp;
2547 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2548 UINT nextcol;
2550 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2551 pmt->hCurrentMenu,
2552 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2553 items[0].text),
2554 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2556 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2558 /* If already displaying a popup, try to display sub-popup */
2560 hmenutmp = pmt->hCurrentMenu;
2561 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2563 /* if subpopup was displayed then we are done */
2564 if (hmenutmp != pmt->hCurrentMenu) return;
2567 /* Check to see if there's another column */
2568 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2569 NO_SELECTED_ITEM ) {
2570 TRACE("Going to %d.\n", nextcol );
2571 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2572 nextcol, TRUE, 0 );
2573 return;
2576 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2578 if( pmt->hCurrentMenu != pmt->hTopMenu )
2580 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2581 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2582 } else hmenutmp = 0;
2584 /* try to move to the next item */
2585 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2586 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2588 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2589 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2590 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2591 pmt->hTopMenu, TRUE, wFlags);
2595 /***********************************************************************
2596 * MENU_TrackMenu
2598 * Menu tracking code.
2600 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2601 HWND hwnd, const RECT *lprect )
2603 MSG msg;
2604 POPUPMENU *menu;
2605 BOOL fRemove;
2606 INT executedMenuId = -1;
2607 MTRACKER mt;
2608 BOOL enterIdleSent = FALSE;
2610 mt.trackFlags = 0;
2611 mt.hCurrentMenu = hmenu;
2612 mt.hTopMenu = hmenu;
2613 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2614 mt.pt.x = x;
2615 mt.pt.y = y;
2617 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2618 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2619 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2621 fEndMenu = FALSE;
2622 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2624 if (wFlags & TPM_BUTTONDOWN)
2626 /* Get the result in order to start the tracking or not */
2627 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2628 fEndMenu = !fRemove;
2631 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2633 while (!fEndMenu)
2635 menu = MENU_GetMenu( mt.hCurrentMenu );
2636 if (!menu) /* sometimes happens if I do a window manager close */
2637 break;
2639 /* we have to keep the message in the queue until it's
2640 * clear that menu loop is not over yet. */
2642 for (;;)
2644 if (PeekMessageA( &msg, 0, 0, 0, PM_NOREMOVE ))
2646 if (!CallMsgFilterA( &msg, MSGF_MENU )) break;
2647 /* remove the message from the queue */
2648 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2650 else
2652 if (!enterIdleSent)
2654 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2655 enterIdleSent = TRUE;
2656 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2658 WaitMessage();
2662 /* check if EndMenu() tried to cancel us, by posting this message */
2663 if(msg.message == WM_CANCELMODE)
2665 /* we are now out of the loop */
2666 fEndMenu = TRUE;
2668 /* remove the message from the queue */
2669 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2671 /* break out of internal loop, ala ESCAPE */
2672 break;
2675 TranslateMessage( &msg );
2676 mt.pt = msg.pt;
2678 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2679 enterIdleSent=FALSE;
2681 fRemove = FALSE;
2682 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2685 * use the mouse coordinates in lParam instead of those in the MSG
2686 * struct to properly handle synthetic messages. lParam coords are
2687 * relative to client area, so they must be converted; since they can
2688 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2690 mt.pt.x = SLOWORD(msg.lParam);
2691 mt.pt.y = SHIWORD(msg.lParam);
2692 ClientToScreen(msg.hwnd,&mt.pt);
2694 /* Find a menu for this mouse event */
2695 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2697 switch(msg.message)
2699 /* no WM_NC... messages in captured state */
2701 case WM_RBUTTONDBLCLK:
2702 case WM_RBUTTONDOWN:
2703 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2704 /* fall through */
2705 case WM_LBUTTONDBLCLK:
2706 case WM_LBUTTONDOWN:
2707 /* If the message belongs to the menu, removes it from the queue */
2708 /* Else, end menu tracking */
2709 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2710 fEndMenu = !fRemove;
2711 break;
2713 case WM_RBUTTONUP:
2714 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2715 /* fall through */
2716 case WM_LBUTTONUP:
2717 /* Check if a menu was selected by the mouse */
2718 if (hmenu)
2720 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2722 /* End the loop if executedMenuId is an item ID */
2723 /* or if the job was done (executedMenuId = 0). */
2724 fEndMenu = fRemove = (executedMenuId != -1);
2726 /* No menu was selected by the mouse */
2727 /* if the function was called by TrackPopupMenu, continue
2728 with the menu tracking. If not, stop it */
2729 else
2730 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2732 break;
2734 case WM_MOUSEMOVE:
2735 /* In win95 winelook, the selected menu item must be changed every time the
2736 mouse moves. In Win31 winelook, the mouse button has to be held down */
2738 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2739 ( (msg.wParam & MK_LBUTTON) ||
2740 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2742 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2744 } /* switch(msg.message) - mouse */
2746 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2748 fRemove = TRUE; /* Keyboard messages are always removed */
2749 switch(msg.message)
2751 case WM_KEYDOWN:
2752 switch(msg.wParam)
2754 case VK_HOME:
2755 case VK_END:
2756 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2757 NO_SELECTED_ITEM, FALSE, 0 );
2758 /* fall through */
2759 case VK_UP:
2760 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2761 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2762 break;
2764 case VK_DOWN: /* If on menu bar, pull-down the menu */
2766 menu = MENU_GetMenu( mt.hCurrentMenu );
2767 if (!(menu->wFlags & MF_POPUP))
2768 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2769 else /* otherwise try to move selection */
2770 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2771 break;
2773 case VK_LEFT:
2774 MENU_KeyLeft( &mt, wFlags );
2775 break;
2777 case VK_RIGHT:
2778 MENU_KeyRight( &mt, wFlags );
2779 break;
2781 case VK_ESCAPE:
2782 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2783 break;
2785 case VK_F1:
2787 HELPINFO hi;
2788 hi.cbSize = sizeof(HELPINFO);
2789 hi.iContextType = HELPINFO_MENUITEM;
2790 if (menu->FocusedItem == NO_SELECTED_ITEM)
2791 hi.iCtrlId = 0;
2792 else
2793 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2794 hi.hItemHandle = hmenu;
2795 hi.dwContextId = menu->dwContextHelpID;
2796 hi.MousePos = msg.pt;
2797 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2798 break;
2801 default:
2802 break;
2804 break; /* WM_KEYDOWN */
2806 case WM_SYSKEYDOWN:
2807 switch(msg.wParam)
2809 case VK_MENU:
2810 fEndMenu = TRUE;
2811 break;
2814 break; /* WM_SYSKEYDOWN */
2816 case WM_CHAR:
2818 UINT pos;
2820 if (msg.wParam == '\r' || msg.wParam == ' ')
2822 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2823 fEndMenu = (executedMenuId != -1);
2825 break;
2828 /* Hack to avoid control chars. */
2829 /* We will find a better way real soon... */
2830 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2832 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2833 LOWORD(msg.wParam), FALSE );
2834 if (pos == (UINT)-2) fEndMenu = TRUE;
2835 else if (pos == (UINT)-1) MessageBeep(0);
2836 else
2838 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2839 TRUE, 0 );
2840 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2841 fEndMenu = (executedMenuId != -1);
2844 break;
2845 } /* switch(msg.message) - kbd */
2847 else
2849 DispatchMessageA( &msg );
2852 if (!fEndMenu) fRemove = TRUE;
2854 /* finally remove message from the queue */
2856 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2857 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2858 else mt.trackFlags &= ~TF_SKIPREMOVE;
2861 ReleaseCapture();
2863 /* If dropdown is still painted and the close box is clicked on
2864 then the menu will be destroyed as part of the DispatchMessage above.
2865 This will then invalidate the menu handle in mt.hTopMenu. We should
2866 check for this first. */
2867 if( IsMenu( mt.hTopMenu ) )
2869 menu = MENU_GetMenu( mt.hTopMenu );
2871 if( IsWindow( mt.hOwnerWnd ) )
2873 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2875 if (menu && menu->wFlags & MF_POPUP)
2877 DestroyWindow( menu->hWnd );
2878 menu->hWnd = 0;
2880 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2881 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2884 /* Reset the variable for hiding menu */
2885 if( menu ) menu->bTimeToHide = FALSE;
2888 /* The return value is only used by TrackPopupMenu */
2889 return ((executedMenuId != -1) ? executedMenuId : 0);
2892 /***********************************************************************
2893 * MENU_InitTracking
2895 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2897 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
2899 HideCaret(0);
2901 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2902 if (!(wFlags & TPM_NONOTIFY))
2903 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2905 SendMessageA( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2907 if (!(wFlags & TPM_NONOTIFY))
2909 POPUPMENU *menu;
2910 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
2911 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2912 { /* app changed/recreated menu bar entries in WM_INITMENU
2913 Recalculate menu sizes else clicks will not work */
2914 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2915 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2919 return TRUE;
2921 /***********************************************************************
2922 * MENU_ExitTracking
2924 static BOOL MENU_ExitTracking(HWND hWnd)
2926 TRACE("hwnd=0x%04x\n", hWnd);
2928 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
2929 ShowCaret(0);
2930 return TRUE;
2933 /***********************************************************************
2934 * MENU_TrackMouseMenuBar
2936 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2938 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2940 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2941 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2943 TRACE("wnd=%x ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2945 if (IsMenu(hMenu))
2947 /* map point to parent client coordinates */
2948 HWND parent = GetAncestor( hWnd, GA_PARENT );
2949 if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
2951 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2952 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2953 MENU_ExitTracking(hWnd);
2958 /***********************************************************************
2959 * MENU_TrackKbdMenuBar
2961 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2963 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, INT vkey)
2965 UINT uItem = NO_SELECTED_ITEM;
2966 HMENU hTrackMenu;
2967 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2969 /* find window that has a menu */
2971 while (GetWindowLongA( hwnd, GWL_STYLE ) & WS_CHILD)
2972 if (!(hwnd = GetParent( hwnd ))) return;
2974 /* check if we have to track a system menu */
2976 hTrackMenu = GetMenu( hwnd );
2977 if (!hTrackMenu || IsIconic(hwnd) || vkey == VK_SPACE )
2979 if (!(GetWindowLongA( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
2980 hTrackMenu = get_win_sys_menu( hwnd );
2981 uItem = 0;
2982 wParam |= HTSYSMENU; /* prevent item lookup */
2985 if (!IsMenu( hTrackMenu )) return;
2987 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
2989 if( vkey && vkey != VK_SPACE )
2991 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, vkey, (wParam & HTSYSMENU) );
2992 if( uItem >= (UINT)(-2) )
2994 if( uItem == (UINT)(-1) ) MessageBeep(0);
2995 hTrackMenu = 0;
2999 if( hTrackMenu )
3001 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3003 if( uItem == NO_SELECTED_ITEM )
3004 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3005 else if( vkey )
3006 PostMessageA( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3008 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3010 MENU_ExitTracking( hwnd );
3014 /**********************************************************************
3015 * TrackPopupMenu (USER32.@)
3017 * Like the win32 API, the function return the command ID only if the
3018 * flag TPM_RETURNCMD is on.
3021 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3022 INT nReserved, HWND hWnd, const RECT *lpRect )
3024 BOOL ret = FALSE;
3026 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3028 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3029 if (!(wFlags & TPM_NONOTIFY))
3030 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3032 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3033 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3034 MENU_ExitTracking(hWnd);
3036 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3037 ret = 1;
3039 return ret;
3042 /**********************************************************************
3043 * TrackPopupMenuEx (USER32.@)
3045 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3046 HWND hWnd, LPTPMPARAMS lpTpm )
3048 FIXME("not fully implemented\n" );
3049 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3050 lpTpm ? &lpTpm->rcExclude : NULL );
3053 /***********************************************************************
3054 * PopupMenuWndProc
3056 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3058 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3060 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3061 hwnd, message, wParam, lParam);
3063 switch(message)
3065 case WM_CREATE:
3067 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3068 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3069 return 0;
3072 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3073 return MA_NOACTIVATE;
3075 case WM_PAINT:
3077 PAINTSTRUCT ps;
3078 BeginPaint( hwnd, &ps );
3079 MENU_DrawPopupMenu( hwnd, ps.hdc,
3080 (HMENU)GetWindowLongA( hwnd, 0 ) );
3081 EndPaint( hwnd, &ps );
3082 return 0;
3084 case WM_ERASEBKGND:
3085 return 1;
3087 case WM_DESTROY:
3088 /* zero out global pointer in case resident popup window was destroyed. */
3089 if (hwnd == top_popup) top_popup = 0;
3090 break;
3092 case WM_SHOWWINDOW:
3094 if( wParam )
3096 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3098 else
3099 SetWindowLongW( hwnd, 0, 0 );
3100 break;
3102 case MM_SETMENUHANDLE:
3103 SetWindowLongW( hwnd, 0, wParam );
3104 break;
3106 case MM_GETMENUHANDLE:
3107 return GetWindowLongW( hwnd, 0 );
3109 default:
3110 return DefWindowProcW( hwnd, message, wParam, lParam );
3112 return 0;
3116 /***********************************************************************
3117 * MENU_GetMenuBarHeight
3119 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3121 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3122 INT orgX, INT orgY )
3124 HDC hdc;
3125 RECT rectBar;
3126 LPPOPUPMENU lppop;
3128 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3129 hwnd, menubarWidth, orgX, orgY );
3131 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3133 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3134 SelectObject( hdc, hMenuFont);
3135 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3136 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3137 ReleaseDC( hwnd, hdc );
3138 return lppop->Height;
3142 /*******************************************************************
3143 * ChangeMenu (USER.153)
3145 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3146 UINT16 id, UINT16 flags )
3148 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3149 hMenu, pos, (DWORD)data, id, flags );
3150 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3151 id, data );
3153 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3154 /* for MF_DELETE. We should check the parameters for all others */
3155 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3157 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3158 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3159 id, data );
3160 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3161 flags & MF_BYPOSITION ? pos : id,
3162 flags & ~MF_REMOVE );
3163 /* Default: MF_INSERT */
3164 return InsertMenu16( hMenu, pos, flags, id, data );
3168 /*******************************************************************
3169 * ChangeMenuA (USER32.@)
3171 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3172 UINT id, UINT flags )
3174 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3175 hMenu, pos, (DWORD)data, id, flags );
3176 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3177 id, data );
3178 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3179 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3180 id, data );
3181 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3182 flags & MF_BYPOSITION ? pos : id,
3183 flags & ~MF_REMOVE );
3184 /* Default: MF_INSERT */
3185 return InsertMenuA( hMenu, pos, flags, id, data );
3189 /*******************************************************************
3190 * ChangeMenuW (USER32.@)
3192 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3193 UINT id, UINT flags )
3195 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3196 hMenu, pos, (DWORD)data, id, flags );
3197 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3198 id, data );
3199 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3200 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3201 id, data );
3202 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3203 flags & MF_BYPOSITION ? pos : id,
3204 flags & ~MF_REMOVE );
3205 /* Default: MF_INSERT */
3206 return InsertMenuW( hMenu, pos, flags, id, data );
3210 /*******************************************************************
3211 * CheckMenuItem (USER.154)
3213 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3215 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3219 /*******************************************************************
3220 * CheckMenuItem (USER32.@)
3222 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3224 MENUITEM *item;
3225 DWORD ret;
3227 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3228 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3229 ret = item->fState & MF_CHECKED;
3230 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3231 else item->fState &= ~MF_CHECKED;
3232 return ret;
3236 /**********************************************************************
3237 * EnableMenuItem (USER.155)
3239 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3241 return EnableMenuItem( hMenu, wItemID, wFlags );
3245 /**********************************************************************
3246 * EnableMenuItem (USER32.@)
3248 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3250 UINT oldflags;
3251 MENUITEM *item;
3252 POPUPMENU *menu;
3254 TRACE("(%04x, %04X, %04X) !\n",
3255 hMenu, wItemID, wFlags);
3257 /* Get the Popupmenu to access the owner menu */
3258 if (!(menu = MENU_GetMenu(hMenu)))
3259 return (UINT)-1;
3261 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3262 return (UINT)-1;
3264 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3265 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3267 /* In win95 if the close item in the system menu change update the close button */
3268 if (TWEAK_WineLook == WIN95_LOOK)
3269 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3271 if (menu->hSysMenuOwner != 0)
3273 POPUPMENU* parentMenu;
3275 /* Get the parent menu to access*/
3276 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3277 return (UINT)-1;
3279 /* Refresh the frame to reflect the change*/
3280 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3281 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3285 return oldflags;
3289 /*******************************************************************
3290 * GetMenuString (USER.161)
3292 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3293 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3295 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3299 /*******************************************************************
3300 * GetMenuStringA (USER32.@)
3302 INT WINAPI GetMenuStringA(
3303 HMENU hMenu, /* [in] menuhandle */
3304 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3305 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3306 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3307 UINT wFlags /* [in] MF_ flags */
3309 MENUITEM *item;
3311 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3312 hMenu, wItemID, str, nMaxSiz, wFlags );
3313 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3314 if (!IS_STRING_ITEM(item->fType)) return 0;
3315 if (!str || !nMaxSiz) return strlenW(item->text);
3316 str[0] = '\0';
3317 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3318 str[nMaxSiz-1] = 0;
3319 TRACE("returning '%s'\n", str );
3320 return strlen(str);
3324 /*******************************************************************
3325 * GetMenuStringW (USER32.@)
3327 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3328 LPWSTR str, INT nMaxSiz, UINT wFlags )
3330 MENUITEM *item;
3332 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3333 hMenu, wItemID, str, nMaxSiz, wFlags );
3334 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3335 if (!IS_STRING_ITEM(item->fType)) return 0;
3336 if (!str || !nMaxSiz) return strlenW(item->text);
3337 str[0] = '\0';
3338 lstrcpynW( str, item->text, nMaxSiz );
3339 return strlenW(str);
3343 /**********************************************************************
3344 * HiliteMenuItem (USER32.@)
3346 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3347 UINT wHilite )
3349 LPPOPUPMENU menu;
3350 TRACE("(%04x, %04x, %04x, %04x);\n",
3351 hWnd, hMenu, wItemID, wHilite);
3352 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3353 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3354 if (menu->FocusedItem == wItemID) return TRUE;
3355 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3356 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3357 return TRUE;
3361 /**********************************************************************
3362 * GetMenuState (USER.250)
3364 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3366 return GetMenuState( hMenu, wItemID, wFlags );
3370 /**********************************************************************
3371 * GetMenuState (USER32.@)
3373 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3375 MENUITEM *item;
3376 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3377 hMenu, wItemID, wFlags);
3378 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3379 debug_print_menuitem (" item: ", item, "");
3380 if (item->fType & MF_POPUP)
3382 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3383 if (!menu) return -1;
3384 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3386 else
3388 /* We used to (from way back then) mask the result to 0xff. */
3389 /* I don't know why and it seems wrong as the documented */
3390 /* return flag MF_SEPARATOR is outside that mask. */
3391 return (item->fType | item->fState);
3396 /**********************************************************************
3397 * GetMenuItemCount (USER.263)
3399 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3401 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3402 if (!menu) return -1;
3403 TRACE("(%04x) returning %d\n",
3404 hMenu, menu->nItems );
3405 return menu->nItems;
3409 /**********************************************************************
3410 * GetMenuItemCount (USER32.@)
3412 INT WINAPI GetMenuItemCount( HMENU hMenu )
3414 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3415 if (!menu) return -1;
3416 TRACE("(%04x) returning %d\n",
3417 hMenu, menu->nItems );
3418 return menu->nItems;
3421 /**********************************************************************
3422 * GetMenuItemID (USER.264)
3424 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3426 return (UINT16) GetMenuItemID (hMenu, nPos);
3429 /**********************************************************************
3430 * GetMenuItemID (USER32.@)
3432 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3434 MENUITEM * lpmi;
3436 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3437 if (lpmi->fType & MF_POPUP) return -1;
3438 return lpmi->wID;
3442 /*******************************************************************
3443 * InsertMenu (USER.410)
3445 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3446 UINT16 id, SEGPTR data )
3448 UINT pos32 = (UINT)pos;
3449 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3450 if (IS_STRING_ITEM(flags) && data)
3451 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3452 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3456 /*******************************************************************
3457 * InsertMenuW (USER32.@)
3459 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3460 UINT id, LPCWSTR str )
3462 MENUITEM *item;
3464 if (IS_STRING_ITEM(flags) && str)
3465 TRACE("hMenu %04x, pos %d, flags %08x, "
3466 "id %04x, str %s\n",
3467 hMenu, pos, flags, id, debugstr_w(str) );
3468 else TRACE("hMenu %04x, pos %d, flags %08x, "
3469 "id %04x, str %08lx (not a string)\n",
3470 hMenu, pos, flags, id, (DWORD)str );
3472 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3474 if (!(MENU_SetItemData( item, flags, id, str )))
3476 RemoveMenu( hMenu, pos, flags );
3477 return FALSE;
3480 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3481 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3483 item->hCheckBit = item->hUnCheckBit = 0;
3484 return TRUE;
3488 /*******************************************************************
3489 * InsertMenuA (USER32.@)
3491 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3492 UINT id, LPCSTR str )
3494 BOOL ret = FALSE;
3496 if (IS_STRING_ITEM(flags) && str)
3498 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3499 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3500 if (newstr)
3502 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3503 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3504 HeapFree( GetProcessHeap(), 0, newstr );
3506 return ret;
3508 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3512 /*******************************************************************
3513 * AppendMenu (USER.411)
3515 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3517 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3521 /*******************************************************************
3522 * AppendMenuA (USER32.@)
3524 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3525 UINT id, LPCSTR data )
3527 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3531 /*******************************************************************
3532 * AppendMenuW (USER32.@)
3534 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3535 UINT id, LPCWSTR data )
3537 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3541 /**********************************************************************
3542 * RemoveMenu (USER.412)
3544 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3546 return RemoveMenu( hMenu, nPos, wFlags );
3550 /**********************************************************************
3551 * RemoveMenu (USER32.@)
3553 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3555 LPPOPUPMENU menu;
3556 MENUITEM *item;
3558 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3559 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3560 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3562 /* Remove item */
3564 MENU_FreeItemData( item );
3566 if (--menu->nItems == 0)
3568 HeapFree( GetProcessHeap(), 0, menu->items );
3569 menu->items = NULL;
3571 else
3573 while(nPos < menu->nItems)
3575 *item = *(item+1);
3576 item++;
3577 nPos++;
3579 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3580 menu->nItems * sizeof(MENUITEM) );
3582 return TRUE;
3586 /**********************************************************************
3587 * DeleteMenu (USER.413)
3589 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3591 return DeleteMenu( hMenu, nPos, wFlags );
3595 /**********************************************************************
3596 * DeleteMenu (USER32.@)
3598 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3600 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3601 if (!item) return FALSE;
3602 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3603 /* nPos is now the position of the item */
3604 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3605 return TRUE;
3609 /*******************************************************************
3610 * ModifyMenu (USER.414)
3612 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3613 UINT16 id, SEGPTR data )
3615 if (IS_STRING_ITEM(flags))
3616 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3617 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3621 /*******************************************************************
3622 * ModifyMenuW (USER32.@)
3624 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3625 UINT id, LPCWSTR str )
3627 MENUITEM *item;
3629 if (IS_STRING_ITEM(flags))
3631 TRACE("%04x %d %04x %04x %s\n",
3632 hMenu, pos, flags, id, debugstr_w(str) );
3633 if (!str) return FALSE;
3635 else
3637 TRACE("%04x %d %04x %04x %08lx\n",
3638 hMenu, pos, flags, id, (DWORD)str );
3641 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3642 return MENU_SetItemData( item, flags, id, str );
3646 /*******************************************************************
3647 * ModifyMenuA (USER32.@)
3649 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3650 UINT id, LPCSTR str )
3652 BOOL ret = FALSE;
3654 if (IS_STRING_ITEM(flags) && str)
3656 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3657 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3658 if (newstr)
3660 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3661 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3662 HeapFree( GetProcessHeap(), 0, newstr );
3664 return ret;
3666 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3670 /**********************************************************************
3671 * CreatePopupMenu (USER.415)
3673 HMENU16 WINAPI CreatePopupMenu16(void)
3675 return CreatePopupMenu();
3679 /**********************************************************************
3680 * CreatePopupMenu (USER32.@)
3682 HMENU WINAPI CreatePopupMenu(void)
3684 HMENU hmenu;
3685 POPUPMENU *menu;
3687 if (!(hmenu = CreateMenu())) return 0;
3688 menu = MENU_GetMenu( hmenu );
3689 menu->wFlags |= MF_POPUP;
3690 menu->bTimeToHide = FALSE;
3691 return hmenu;
3695 /**********************************************************************
3696 * GetMenuCheckMarkDimensions (USER.417)
3697 * GetMenuCheckMarkDimensions (USER32.@)
3699 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3701 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3705 /**********************************************************************
3706 * SetMenuItemBitmaps (USER.418)
3708 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3709 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3711 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3715 /**********************************************************************
3716 * SetMenuItemBitmaps (USER32.@)
3718 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3719 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3721 MENUITEM *item;
3722 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3723 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3724 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3726 if (!hNewCheck && !hNewUnCheck)
3728 item->fState &= ~MF_USECHECKBITMAPS;
3730 else /* Install new bitmaps */
3732 item->hCheckBit = hNewCheck;
3733 item->hUnCheckBit = hNewUnCheck;
3734 item->fState |= MF_USECHECKBITMAPS;
3736 return TRUE;
3740 /**********************************************************************
3741 * CreateMenu (USER.151)
3743 HMENU16 WINAPI CreateMenu16(void)
3745 return CreateMenu();
3749 /**********************************************************************
3750 * CreateMenu (USER32.@)
3752 HMENU WINAPI CreateMenu(void)
3754 HMENU hMenu;
3755 LPPOPUPMENU menu;
3756 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3757 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3759 ZeroMemory(menu, sizeof(POPUPMENU));
3760 menu->wMagic = MENU_MAGIC;
3761 menu->FocusedItem = NO_SELECTED_ITEM;
3762 menu->bTimeToHide = FALSE;
3764 TRACE("return %04x\n", hMenu );
3766 return hMenu;
3770 /**********************************************************************
3771 * DestroyMenu (USER.152)
3773 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3775 return DestroyMenu( hMenu );
3779 /**********************************************************************
3780 * DestroyMenu (USER32.@)
3782 BOOL WINAPI DestroyMenu( HMENU hMenu )
3784 TRACE("(%04x)\n", hMenu);
3786 /* Silently ignore attempts to destroy default system popup */
3788 if (hMenu && hMenu != MENU_DefSysPopup)
3790 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3792 if (!lppop) return FALSE;
3794 lppop->wMagic = 0; /* Mark it as destroyed */
3796 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3798 DestroyWindow( lppop->hWnd );
3799 lppop->hWnd = 0;
3802 if (lppop->items) /* recursively destroy submenus */
3804 int i;
3805 MENUITEM *item = lppop->items;
3806 for (i = lppop->nItems; i > 0; i--, item++)
3808 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3809 MENU_FreeItemData( item );
3811 HeapFree( GetProcessHeap(), 0, lppop->items );
3813 USER_HEAP_FREE( hMenu );
3815 return (hMenu != MENU_DefSysPopup);
3819 /**********************************************************************
3820 * GetSystemMenu (USER32.@)
3822 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3824 WND *wndPtr = WIN_FindWndPtr( hWnd );
3825 HMENU retvalue = 0;
3827 if (wndPtr)
3829 if( wndPtr->hSysMenu )
3831 if( bRevert )
3833 DestroyMenu(wndPtr->hSysMenu);
3834 wndPtr->hSysMenu = 0;
3836 else
3838 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3839 if( menu )
3841 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3842 menu->items[0].hSubMenu = MENU_CopySysPopup();
3844 else
3846 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
3847 wndPtr->hSysMenu, hWnd);
3848 wndPtr->hSysMenu = 0;
3853 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3854 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3856 if( wndPtr->hSysMenu )
3858 POPUPMENU *menu;
3859 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3861 /* Store the dummy sysmenu handle to facilitate the refresh */
3862 /* of the close button if the SC_CLOSE item change */
3863 menu = MENU_GetMenu(retvalue);
3864 if ( menu )
3865 menu->hSysMenuOwner = wndPtr->hSysMenu;
3867 WIN_ReleaseWndPtr(wndPtr);
3869 return bRevert ? 0 : retvalue;
3873 /*******************************************************************
3874 * SetSystemMenu (USER32.@)
3876 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3878 WND *wndPtr = WIN_FindWndPtr(hwnd);
3880 if (wndPtr)
3882 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3883 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3884 WIN_ReleaseWndPtr(wndPtr);
3885 return TRUE;
3887 return FALSE;
3891 /**********************************************************************
3892 * GetMenu (USER32.@)
3894 HMENU WINAPI GetMenu( HWND hWnd )
3896 HMENU retvalue = (HMENU)GetWindowLongA( hWnd, GWL_ID );
3897 TRACE("for %04x returning %04x\n", hWnd, retvalue);
3898 return retvalue;
3902 /**********************************************************************
3903 * SetMenu (USER32.@)
3905 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3907 TRACE("(%04x, %04x);\n", hWnd, hMenu);
3909 if (hMenu && !IsMenu(hMenu))
3911 WARN("hMenu %x is not a menu handle\n", hMenu);
3912 return FALSE;
3914 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3916 hWnd = WIN_GetFullHandle( hWnd );
3917 if (GetCapture() == hWnd) ReleaseCapture();
3919 if (hMenu != 0)
3921 LPPOPUPMENU lpmenu;
3923 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3925 lpmenu->hWnd = hWnd;
3926 lpmenu->Height = 0; /* Make sure we recalculate the size */
3928 SetWindowLongA( hWnd, GWL_ID, hMenu );
3930 if (IsWindowVisible(hWnd))
3931 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3932 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3933 return TRUE;
3938 /**********************************************************************
3939 * GetSubMenu (USER.159)
3941 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
3943 return GetSubMenu( hMenu, nPos );
3947 /**********************************************************************
3948 * GetSubMenu (USER32.@)
3950 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3952 MENUITEM * lpmi;
3954 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3955 if (!(lpmi->fType & MF_POPUP)) return 0;
3956 return lpmi->hSubMenu;
3960 /**********************************************************************
3961 * DrawMenuBar (USER32.@)
3963 BOOL WINAPI DrawMenuBar( HWND hWnd )
3965 LPPOPUPMENU lppop;
3966 HMENU hMenu = GetMenu(hWnd);
3968 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3969 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3971 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3972 lppop->hwndOwner = hWnd;
3973 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3974 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3975 return TRUE;
3978 /***********************************************************************
3979 * DrawMenuBarTemp (USER32.@)
3981 DWORD WINAPI DrawMenuBarTemp(DWORD p1, DWORD p2)
3983 FIXME("(%08lx %08lx): stub\n", p1, p2);
3984 return 0;
3987 /***********************************************************************
3988 * EndMenu (USER.187)
3989 * EndMenu (USER32.@)
3991 void WINAPI EndMenu(void)
3993 /* if we are in the menu code, and it is active */
3994 if (!fEndMenu && top_popup)
3996 /* terminate the menu handling code */
3997 fEndMenu = TRUE;
3999 /* needs to be posted to wakeup the internal menu handler */
4000 /* which will now terminate the menu, in the event that */
4001 /* the main window was minimized, or lost focus, so we */
4002 /* don't end up with an orphaned menu */
4003 PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
4008 /***********************************************************************
4009 * LookupMenuHandle (USER.217)
4011 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4013 HMENU hmenu32 = hmenu;
4014 UINT id32 = id;
4015 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4016 else return hmenu32;
4020 /**********************************************************************
4021 * LoadMenu (USER.150)
4023 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4025 HRSRC16 hRsrc;
4026 HGLOBAL16 handle;
4027 HMENU16 hMenu;
4029 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4031 if (HIWORD(name))
4033 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4036 if (!name) return 0;
4038 /* check for Win32 module */
4039 if (HIWORD(instance)) return LoadMenuA( instance, name );
4040 instance = GetExePtr( instance );
4042 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4043 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4044 hMenu = LoadMenuIndirect16(LockResource16(handle));
4045 FreeResource16( handle );
4046 return hMenu;
4050 /*****************************************************************
4051 * LoadMenuA (USER32.@)
4053 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4055 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4056 if (!hrsrc) return 0;
4057 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4061 /*****************************************************************
4062 * LoadMenuW (USER32.@)
4064 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4066 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4067 if (!hrsrc) return 0;
4068 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4072 /**********************************************************************
4073 * LoadMenuIndirect (USER.220)
4075 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4077 HMENU16 hMenu;
4078 WORD version, offset;
4079 LPCSTR p = (LPCSTR)template;
4081 TRACE("(%p)\n", template );
4082 version = GET_WORD(p);
4083 p += sizeof(WORD);
4084 if (version)
4086 WARN("version must be 0 for Win16\n" );
4087 return 0;
4089 offset = GET_WORD(p);
4090 p += sizeof(WORD) + offset;
4091 if (!(hMenu = CreateMenu())) return 0;
4092 if (!MENU_ParseResource( p, hMenu, FALSE ))
4094 DestroyMenu( hMenu );
4095 return 0;
4097 return hMenu;
4101 /**********************************************************************
4102 * LoadMenuIndirectA (USER32.@)
4104 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4106 HMENU16 hMenu;
4107 WORD version, offset;
4108 LPCSTR p = (LPCSTR)template;
4110 TRACE("%p\n", template );
4111 version = GET_WORD(p);
4112 p += sizeof(WORD);
4113 switch (version)
4115 case 0:
4116 offset = GET_WORD(p);
4117 p += sizeof(WORD) + offset;
4118 if (!(hMenu = CreateMenu())) return 0;
4119 if (!MENU_ParseResource( p, hMenu, TRUE ))
4121 DestroyMenu( hMenu );
4122 return 0;
4124 return hMenu;
4125 case 1:
4126 offset = GET_WORD(p);
4127 p += sizeof(WORD) + offset;
4128 if (!(hMenu = CreateMenu())) return 0;
4129 if (!MENUEX_ParseResource( p, hMenu))
4131 DestroyMenu( hMenu );
4132 return 0;
4134 return hMenu;
4135 default:
4136 ERR("version %d not supported.\n", version);
4137 return 0;
4142 /**********************************************************************
4143 * LoadMenuIndirectW (USER32.@)
4145 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4147 /* FIXME: is there anything different between A and W? */
4148 return LoadMenuIndirectA( template );
4152 /**********************************************************************
4153 * IsMenu (USER.358)
4155 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4157 return IsMenu( hmenu );
4161 /**********************************************************************
4162 * IsMenu (USER32.@)
4164 BOOL WINAPI IsMenu(HMENU hmenu)
4166 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4167 return menu != NULL;
4170 /**********************************************************************
4171 * GetMenuItemInfo_common
4174 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4175 LPMENUITEMINFOW lpmii, BOOL unicode)
4177 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4179 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4181 if (!menu)
4182 return FALSE;
4184 if (lpmii->fMask & MIIM_TYPE) {
4185 lpmii->fType = menu->fType;
4186 switch (MENU_ITEM_TYPE(menu->fType)) {
4187 case MF_STRING:
4188 break; /* will be done below */
4189 case MF_OWNERDRAW:
4190 case MF_BITMAP:
4191 lpmii->dwTypeData = menu->text;
4192 /* fall through */
4193 default:
4194 lpmii->cch = 0;
4198 /* copy the text string */
4199 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4200 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4202 int len;
4203 if (unicode)
4205 len = strlenW(menu->text);
4206 if(lpmii->dwTypeData && lpmii->cch)
4207 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4209 else
4211 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4212 if(lpmii->dwTypeData && lpmii->cch)
4213 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4214 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4215 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4217 /* if we've copied a substring we return its length */
4218 if(lpmii->dwTypeData && lpmii->cch)
4220 if (lpmii->cch <= len) lpmii->cch--;
4222 else /* return length of string */
4223 lpmii->cch = len;
4226 if (lpmii->fMask & MIIM_FTYPE)
4227 lpmii->fType = menu->fType;
4229 if (lpmii->fMask & MIIM_BITMAP)
4230 lpmii->hbmpItem = menu->hbmpItem;
4232 if (lpmii->fMask & MIIM_STATE)
4233 lpmii->fState = menu->fState;
4235 if (lpmii->fMask & MIIM_ID)
4236 lpmii->wID = menu->wID;
4238 if (lpmii->fMask & MIIM_SUBMENU)
4239 lpmii->hSubMenu = menu->hSubMenu;
4241 if (lpmii->fMask & MIIM_CHECKMARKS) {
4242 lpmii->hbmpChecked = menu->hCheckBit;
4243 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4245 if (lpmii->fMask & MIIM_DATA)
4246 lpmii->dwItemData = menu->dwItemData;
4248 return TRUE;
4251 /**********************************************************************
4252 * GetMenuItemInfoA (USER32.@)
4254 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4255 LPMENUITEMINFOA lpmii)
4257 return GetMenuItemInfo_common (hmenu, item, bypos,
4258 (LPMENUITEMINFOW)lpmii, FALSE);
4261 /**********************************************************************
4262 * GetMenuItemInfoW (USER32.@)
4264 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4265 LPMENUITEMINFOW lpmii)
4267 return GetMenuItemInfo_common (hmenu, item, bypos,
4268 lpmii, TRUE);
4272 /* set a menu item text from a ASCII or Unicode string */
4273 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4275 if (!text)
4277 menu->text = NULL;
4278 menu->fType |= MF_SEPARATOR;
4280 else if (unicode)
4282 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4283 strcpyW( menu->text, text );
4285 else
4287 LPCSTR str = (LPCSTR)text;
4288 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4289 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4290 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4295 /**********************************************************************
4296 * SetMenuItemInfo_common
4299 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4300 const MENUITEMINFOW *lpmii,
4301 BOOL unicode)
4303 if (!menu) return FALSE;
4305 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4307 if (lpmii->fMask & MIIM_TYPE ) {
4308 /* Get rid of old string. */
4309 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4310 HeapFree(GetProcessHeap(), 0, menu->text);
4311 menu->text = NULL;
4314 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4315 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4316 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4318 menu->text = lpmii->dwTypeData;
4320 if (IS_STRING_ITEM(menu->fType))
4321 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4324 if (lpmii->fMask & MIIM_FTYPE ) {
4325 /* free the string when the type is changing */
4326 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4327 HeapFree(GetProcessHeap(), 0, menu->text);
4328 menu->text = NULL;
4330 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4331 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4332 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4333 menu->fType |= MF_SEPARATOR;
4336 if (lpmii->fMask & MIIM_STRING ) {
4337 /* free the string when used */
4338 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4339 HeapFree(GetProcessHeap(), 0, menu->text);
4340 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4344 if (lpmii->fMask & MIIM_STATE)
4346 /* fixme: MFS_DEFAULT do we have to reset the other menu items? */
4347 menu->fState = lpmii->fState;
4350 if (lpmii->fMask & MIIM_ID)
4351 menu->wID = lpmii->wID;
4353 if (lpmii->fMask & MIIM_SUBMENU) {
4354 menu->hSubMenu = lpmii->hSubMenu;
4355 if (menu->hSubMenu) {
4356 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4357 if (subMenu) {
4358 subMenu->wFlags |= MF_POPUP;
4359 menu->fType |= MF_POPUP;
4361 else
4362 /* FIXME: Return an error ? */
4363 menu->fType &= ~MF_POPUP;
4365 else
4366 menu->fType &= ~MF_POPUP;
4369 if (lpmii->fMask & MIIM_CHECKMARKS)
4371 if (lpmii->fType & MFT_RADIOCHECK)
4372 menu->fType |= MFT_RADIOCHECK;
4374 menu->hCheckBit = lpmii->hbmpChecked;
4375 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4377 if (lpmii->fMask & MIIM_DATA)
4378 menu->dwItemData = lpmii->dwItemData;
4380 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4381 return TRUE;
4384 /**********************************************************************
4385 * SetMenuItemInfoA (USER32.@)
4387 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4388 const MENUITEMINFOA *lpmii)
4390 if ((lpmii->fType & (MF_HILITE|MF_POPUP)) || (lpmii->fState)) {
4391 /* QuickTime does pass invalid data into SetMenuItemInfo.
4392 * do some of the checks Windows does.
4394 WARN("Bad masks for type (0x%08x) or state (0x%08x)\n",
4395 lpmii->fType,lpmii->fState );
4396 return FALSE;
4399 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4400 (const MENUITEMINFOW *)lpmii, FALSE);
4403 /**********************************************************************
4404 * SetMenuItemInfoW (USER32.@)
4406 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4407 const MENUITEMINFOW *lpmii)
4409 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4410 lpmii, TRUE);
4413 /**********************************************************************
4414 * SetMenuDefaultItem (USER32.@)
4417 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4419 UINT i;
4420 POPUPMENU *menu;
4421 MENUITEM *item;
4423 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4425 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4427 /* reset all default-item flags */
4428 item = menu->items;
4429 for (i = 0; i < menu->nItems; i++, item++)
4431 item->fState &= ~MFS_DEFAULT;
4434 /* no default item */
4435 if ( -1 == uItem)
4437 return TRUE;
4440 item = menu->items;
4441 if ( bypos )
4443 if ( uItem >= menu->nItems ) return FALSE;
4444 item[uItem].fState |= MFS_DEFAULT;
4445 return TRUE;
4447 else
4449 for (i = 0; i < menu->nItems; i++, item++)
4451 if (item->wID == uItem)
4453 item->fState |= MFS_DEFAULT;
4454 return TRUE;
4459 return FALSE;
4462 /**********************************************************************
4463 * GetMenuDefaultItem (USER32.@)
4465 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4467 POPUPMENU *menu;
4468 MENUITEM * item;
4469 UINT i = 0;
4471 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4473 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4475 /* find default item */
4476 item = menu->items;
4478 /* empty menu */
4479 if (! item) return -1;
4481 while ( !( item->fState & MFS_DEFAULT ) )
4483 i++; item++;
4484 if (i >= menu->nItems ) return -1;
4487 /* default: don't return disabled items */
4488 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4490 /* search rekursiv when needed */
4491 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4493 UINT ret;
4494 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4495 if ( -1 != ret ) return ret;
4497 /* when item not found in submenu, return the popup item */
4499 return ( bypos ) ? i : item->wID;
4503 /*******************************************************************
4504 * InsertMenuItem (USER.441)
4506 * FIXME: untested
4508 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4509 const MENUITEMINFO16 *mii )
4511 MENUITEMINFOA miia;
4513 miia.cbSize = sizeof(miia);
4514 miia.fMask = mii->fMask;
4515 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4516 miia.fType = mii->fType;
4517 miia.fState = mii->fState;
4518 miia.wID = mii->wID;
4519 miia.hSubMenu = mii->hSubMenu;
4520 miia.hbmpChecked = mii->hbmpChecked;
4521 miia.hbmpUnchecked = mii->hbmpUnchecked;
4522 miia.dwItemData = mii->dwItemData;
4523 miia.cch = mii->cch;
4524 if (IS_STRING_ITEM(miia.fType))
4525 miia.dwTypeData = MapSL(mii->dwTypeData);
4526 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4530 /**********************************************************************
4531 * InsertMenuItemA (USER32.@)
4533 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4534 const MENUITEMINFOA *lpmii)
4536 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4537 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4541 /**********************************************************************
4542 * InsertMenuItemW (USER32.@)
4544 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4545 const MENUITEMINFOW *lpmii)
4547 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4548 return SetMenuItemInfo_common(item, lpmii, TRUE);
4551 /**********************************************************************
4552 * CheckMenuRadioItem (USER32.@)
4555 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4556 UINT first, UINT last, UINT check,
4557 UINT bypos)
4559 MENUITEM *mifirst, *milast, *micheck;
4560 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4562 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4563 hMenu, first, last, check, bypos);
4565 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4566 milast = MENU_FindItem (&mlast, &last, bypos);
4567 micheck = MENU_FindItem (&mcheck, &check, bypos);
4569 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4570 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4571 micheck > milast || micheck < mifirst)
4572 return FALSE;
4574 while (mifirst <= milast)
4576 if (mifirst == micheck)
4578 mifirst->fType |= MFT_RADIOCHECK;
4579 mifirst->fState |= MFS_CHECKED;
4580 } else {
4581 mifirst->fType &= ~MFT_RADIOCHECK;
4582 mifirst->fState &= ~MFS_CHECKED;
4584 mifirst++;
4587 return TRUE;
4590 /**********************************************************************
4591 * CheckMenuRadioItem (USER.666)
4593 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4594 UINT16 first, UINT16 last, UINT16 check,
4595 BOOL16 bypos)
4597 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4600 /**********************************************************************
4601 * GetMenuItemRect (USER32.@)
4603 * ATTENTION: Here, the returned values in rect are the screen
4604 * coordinates of the item just like if the menu was
4605 * always on the upper left side of the application.
4608 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4609 LPRECT rect)
4611 POPUPMENU *itemMenu;
4612 MENUITEM *item;
4613 HWND referenceHwnd;
4615 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4617 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4618 referenceHwnd = hwnd;
4620 if(!hwnd)
4622 itemMenu = MENU_GetMenu(hMenu);
4623 if (itemMenu == NULL)
4624 return FALSE;
4626 if(itemMenu->hWnd == 0)
4627 return FALSE;
4628 referenceHwnd = itemMenu->hWnd;
4631 if ((rect == NULL) || (item == NULL))
4632 return FALSE;
4634 *rect = item->rect;
4636 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4638 return TRUE;
4642 /**********************************************************************
4643 * SetMenuInfo (USER32.@)
4645 * FIXME
4646 * MIM_APPLYTOSUBMENUS
4647 * actually use the items to draw the menu
4649 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4651 POPUPMENU *menu;
4653 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4655 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4658 if (lpmi->fMask & MIM_BACKGROUND)
4659 menu->hbrBack = lpmi->hbrBack;
4661 if (lpmi->fMask & MIM_HELPID)
4662 menu->dwContextHelpID = lpmi->dwContextHelpID;
4664 if (lpmi->fMask & MIM_MAXHEIGHT)
4665 menu->cyMax = lpmi->cyMax;
4667 if (lpmi->fMask & MIM_MENUDATA)
4668 menu->dwMenuData = lpmi->dwMenuData;
4670 if (lpmi->fMask & MIM_STYLE)
4671 menu->dwStyle = lpmi->dwStyle;
4673 return TRUE;
4675 return FALSE;
4678 /**********************************************************************
4679 * GetMenuInfo (USER32.@)
4681 * NOTES
4682 * win98/NT5.0
4685 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4686 { POPUPMENU *menu;
4688 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4690 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4693 if (lpmi->fMask & MIM_BACKGROUND)
4694 lpmi->hbrBack = menu->hbrBack;
4696 if (lpmi->fMask & MIM_HELPID)
4697 lpmi->dwContextHelpID = menu->dwContextHelpID;
4699 if (lpmi->fMask & MIM_MAXHEIGHT)
4700 lpmi->cyMax = menu->cyMax;
4702 if (lpmi->fMask & MIM_MENUDATA)
4703 lpmi->dwMenuData = menu->dwMenuData;
4705 if (lpmi->fMask & MIM_STYLE)
4706 lpmi->dwStyle = menu->dwStyle;
4708 return TRUE;
4710 return FALSE;
4713 /**********************************************************************
4714 * SetMenuContextHelpId (USER.384)
4716 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4718 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4722 /**********************************************************************
4723 * SetMenuContextHelpId (USER32.@)
4725 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4727 LPPOPUPMENU menu;
4729 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4731 if ((menu = MENU_GetMenu(hMenu)))
4733 menu->dwContextHelpID = dwContextHelpID;
4734 return TRUE;
4736 return FALSE;
4739 /**********************************************************************
4740 * GetMenuContextHelpId (USER.385)
4742 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4744 return GetMenuContextHelpId( hMenu );
4747 /**********************************************************************
4748 * GetMenuContextHelpId (USER32.@)
4750 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4752 LPPOPUPMENU menu;
4754 TRACE("(0x%04x)\n", hMenu);
4756 if ((menu = MENU_GetMenu(hMenu)))
4758 return menu->dwContextHelpID;
4760 return 0;
4763 /**********************************************************************
4764 * MenuItemFromPoint (USER32.@)
4766 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4768 FIXME("(0x%04x,0x%04x,(%ld,%ld)):stub\n",
4769 hWnd, hMenu, ptScreen.x, ptScreen.y);
4770 return 0;
4774 /**********************************************************************
4775 * translate_accelerator
4777 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4778 BYTE fVirt, WORD key, WORD cmd )
4780 UINT mesg = 0;
4782 if (wParam != key) return FALSE;
4784 if (message == WM_CHAR)
4786 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4788 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4789 goto found;
4792 else
4794 if(fVirt & FVIRTKEY)
4796 INT mask = 0;
4797 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4798 wParam, 0xff & HIWORD(lParam));
4799 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4800 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4801 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4802 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4803 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4805 else
4807 if (!(lParam & 0x01000000)) /* no special_key */
4809 if ((fVirt & FALT) && (lParam & 0x20000000))
4810 { /* ^^ ALT pressed */
4811 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4812 goto found;
4817 return FALSE;
4819 found:
4820 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4821 mesg = 1;
4822 else if (GetCapture())
4823 mesg = 2;
4824 else if (!IsWindowEnabled(hWnd))
4825 mesg = 3;
4826 else
4828 HMENU hMenu, hSubMenu, hSysMenu;
4829 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4831 hMenu = (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4832 hSysMenu = get_win_sys_menu( hWnd );
4834 /* find menu item and ask application to initialize it */
4835 /* 1. in the system menu */
4836 hSubMenu = hSysMenu;
4837 nPos = cmd;
4838 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4840 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4841 if(hSubMenu != hSysMenu)
4843 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4844 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4845 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4847 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4849 else /* 2. in the window's menu */
4851 hSubMenu = hMenu;
4852 nPos = cmd;
4853 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4855 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4856 if(hSubMenu != hMenu)
4858 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4859 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
4860 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4862 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4866 if (uSysStat != (UINT)-1)
4868 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4869 mesg=4;
4870 else
4871 mesg=WM_SYSCOMMAND;
4873 else
4875 if (uStat != (UINT)-1)
4877 if (IsIconic(hWnd))
4878 mesg=5;
4879 else
4881 if (uStat & (MF_DISABLED|MF_GRAYED))
4882 mesg=6;
4883 else
4884 mesg=WM_COMMAND;
4887 else
4888 mesg=WM_COMMAND;
4892 if( mesg==WM_COMMAND )
4894 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4895 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
4897 else if( mesg==WM_SYSCOMMAND )
4899 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4900 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
4902 else
4904 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4905 * #0: unknown (please report!)
4906 * #1: for WM_KEYUP,WM_SYSKEYUP
4907 * #2: mouse is captured
4908 * #3: window is disabled
4909 * #4: it's a disabled system menu option
4910 * #5: it's a menu option, but window is iconic
4911 * #6: it's a menu option, but disabled
4913 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4914 if(mesg==0)
4915 ERR_(accel)(" unknown reason - please report!");
4917 return TRUE;
4920 /**********************************************************************
4921 * TranslateAccelerator (USER32.@)
4922 * TranslateAcceleratorA (USER32.@)
4923 * TranslateAcceleratorW (USER32.@)
4925 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
4927 /* YES, Accel16! */
4928 LPACCEL16 lpAccelTbl;
4929 int i;
4931 if (msg == NULL)
4933 WARN_(accel)("msg null; should hang here to be win compatible\n");
4934 return 0;
4936 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
4938 WARN_(accel)("invalid accel handle=%x\n", hAccel);
4939 return 0;
4941 if ((msg->message != WM_KEYDOWN &&
4942 msg->message != WM_KEYUP &&
4943 msg->message != WM_SYSKEYDOWN &&
4944 msg->message != WM_SYSKEYUP &&
4945 msg->message != WM_CHAR)) return 0;
4947 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
4948 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4949 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4951 i = 0;
4954 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4955 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4956 return 1;
4957 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4958 WARN_(accel)("couldn't translate accelerator key\n");
4959 return 0;